aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--hwdef-BLF_GT.h60
-rw-r--r--hwdef-BLF_GT_Mini.h14
-rw-r--r--hwdef-BLF_LT1-t1616.h107
-rw-r--r--hwdef-BLF_LT1.h58
-rw-r--r--hwdef-BLF_Q8-T1616.h107
-rw-r--r--hwdef-Emisar_D1.h9
-rw-r--r--hwdef-Emisar_D18.h48
-rw-r--r--hwdef-Emisar_D1S.h9
-rw-r--r--hwdef-Emisar_D4.h47
-rw-r--r--hwdef-Emisar_D4S.h14
-rw-r--r--hwdef-Emisar_D4Sv2-tintramp.h188
-rw-r--r--hwdef-Emisar_D4Sv2.h122
-rw-r--r--hwdef-Emisar_D4v2.h104
-rw-r--r--hwdef-FW3A.h50
-rw-r--r--hwdef-Mateminco_MF01-Mini.h49
-rw-r--r--hwdef-Mateminco_MF01S.h60
-rw-r--r--hwdef-Mateminco_MT35-Mini.h47
-rw-r--r--hwdef-Noctigon_DM11-12V.h165
-rw-r--r--hwdef-Noctigon_DM11-SBT90.h157
-rw-r--r--hwdef-Noctigon_DM11.h156
-rw-r--r--hwdef-Noctigon_K1-12V.h138
-rw-r--r--hwdef-Noctigon_K1-SBT90.h143
-rw-r--r--hwdef-Noctigon_K1.h134
-rw-r--r--hwdef-Noctigon_K9.3.h164
-rw-r--r--hwdef-Noctigon_KR4-12V.h157
-rw-r--r--hwdef-Noctigon_KR4.h161
-rw-r--r--hwdef-TK_Saber.h9
-rw-r--r--hwdef-blf-gt-mini.h14
-rw-r--r--hwdef-blf-gt.h102
-rw-r--r--hwdef-blf-lt1-t1616.c210
-rw-r--r--hwdef-blf-lt1-t1616.h161
-rw-r--r--hwdef-blf-lt1.c204
-rw-r--r--hwdef-blf-lt1.h114
-rw-r--r--hwdef-blf-q8-t1616.h138
-rw-r--r--hwdef-blf-q8.h (renamed from hwdef-BLF_Q8.h)13
-rw-r--r--hwdef-emisar-2ch-fet.c206
-rw-r--r--hwdef-emisar-2ch-fet.h209
-rw-r--r--hwdef-emisar-2ch.c193
-rw-r--r--hwdef-emisar-2ch.h210
-rw-r--r--hwdef-emisar-d18.h101
-rw-r--r--hwdef-emisar-d4.c59
-rw-r--r--hwdef-emisar-d4.h104
-rw-r--r--hwdef-emisar-d4k-3ch.c362
-rw-r--r--hwdef-emisar-d4k-3ch.h248
-rw-r--r--hwdef-emisar-d4s.h13
-rw-r--r--hwdef-emisar-d4sv2.c67
-rw-r--r--hwdef-emisar-d4sv2.h179
-rw-r--r--hwdef-emisar-d4v2-nofet.c55
-rw-r--r--hwdef-emisar-d4v2.c61
-rw-r--r--hwdef-emisar-d4v2.h171
-rw-r--r--hwdef-ff-pl47.h (renamed from hwdef-FF_PL47.h)13
-rw-r--r--hwdef-ff-rot66.h (renamed from hwdef-FF_ROT66.h)13
-rw-r--r--hwdef-fw3a.c71
-rw-r--r--hwdef-fw3a.h105
-rw-r--r--hwdef-fw3x-lume1.c60
-rw-r--r--hwdef-fw3x-lume1.h175
-rw-r--r--hwdef-gchart-fet1-t1616.h120
-rw-r--r--hwdef-mateminco-mf01-mini.h100
-rw-r--r--hwdef-mateminco-mf01s.h102
-rw-r--r--hwdef-mateminco-mt35-mini.h93
-rw-r--r--hwdef-noctigon-dm11-boost.c97
-rw-r--r--hwdef-noctigon-dm11-boost.h206
-rw-r--r--hwdef-noctigon-dm11.h183
-rw-r--r--hwdef-noctigon-k1-boost.h188
-rw-r--r--hwdef-noctigon-k1-sbt90.h177
-rw-r--r--hwdef-noctigon-k1.c65
-rw-r--r--hwdef-noctigon-k1.h170
-rw-r--r--hwdef-noctigon-kr4-2ch.h (renamed from hwdef-Noctigon_KR4-tintramp.h)15
-rw-r--r--hwdef-noctigon-kr4-boost.h57
-rw-r--r--hwdef-noctigon-kr4-nofet.c60
-rw-r--r--hwdef-noctigon-kr4.c63
-rw-r--r--hwdef-noctigon-kr4.h194
-rw-r--r--hwdef-noctigon-m44.c262
-rw-r--r--hwdef-noctigon-m44.h206
-rw-r--r--hwdef-sofirn-lt1s-pro.c237
-rw-r--r--hwdef-sofirn-lt1s-pro.h151
-rw-r--r--hwdef-sofirn-sp10-pro.c63
-rw-r--r--hwdef-sofirn-sp10-pro.h (renamed from hwdef-Sofirn_SP10-Pro.h)135
-rwxr-xr-xhwdef-thefreeman-boost-fwaa-mp3432-hdr-dac-rgb.h187
-rw-r--r--hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.c90
-rw-r--r--hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.h181
-rw-r--r--hwdef-thefreeman-lin16dac.c92
-rw-r--r--hwdef-thefreeman-lin16dac.h134
-rw-r--r--hwdef-wurkkos-ts10.c65
-rw-r--r--hwdef-wurkkos-ts10.h136
-rw-r--r--hwdef-wurkkos-ts25.c62
-rw-r--r--hwdef-wurkkos-ts25.h144
87 files changed, 7412 insertions, 2731 deletions
diff --git a/hwdef-BLF_GT.h b/hwdef-BLF_GT.h
deleted file mode 100644
index 51b391d..0000000
--- a/hwdef-BLF_GT.h
+++ /dev/null
@@ -1,60 +0,0 @@
-#ifndef HWDEF_BLF_GT_H
-#define HWDEF_BLF_GT_H
-
-/* BLF GT driver layout
- * ----
- * Reset -|1 8|- VCC (unused)
- * eswitch -|2 7|- Voltage divider
- * AUX LED -|3 6|- Current control (buck level)
- * GND -|4 5|- PWM (buck output on/off)
- * ----
- */
-
-#define PWM_CHANNELS 2
-
-#ifndef AUXLED_PIN
-#define AUXLED_PIN PB4 // pin 3
-#endif
-
-#ifndef SWITCH_PIN
-#define SWITCH_PIN PB3 // pin 2
-#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt
-#endif
-
-#ifndef PWM1_PIN
-#define PWM1_PIN PB0 // pin 5, 1x7135 PWM
-#define PWM1_LVL OCR0A // OCR0A is the output compare register for PB0
-#endif
-#ifndef PWM2_PIN
-#define PWM2_PIN PB1 // pin 6, FET PWM
-#define PWM2_LVL OCR0B // OCR0B is the output compare register for PB1
-#endif
-
-#define USE_VOLTAGE_DIVIDER // use a voltage divider on pin 7, not VCC
-#ifndef VOLTAGE_PIN
-#define VOLTAGE_PIN PB2 // pin 7, voltage ADC
-#define VOLTAGE_CHANNEL 0x01 // MUX 01 corresponds with PB2
-#define VOLTAGE_ADC ADC1D // Digital input disable bit corresponding with PB2
-// inherited from tk-attiny.h
-//#define VOLTAGE_ADC_DIDR DIDR0 // DIDR for ADC1
-// 1.1V reference, left-adjust, ADC1/PB2
-//#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)
-#endif
-#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)
-#endif
-#ifndef ADC_22
-#define ADC_22 (92*4)
-#endif
-
-#define FAST 0xA3 // fast PWM both channels
-#define PHASE 0xA1 // phase-correct PWM both channels
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-BLF_GT_Mini.h b/hwdef-BLF_GT_Mini.h
deleted file mode 100644
index 43f74f3..0000000
--- a/hwdef-BLF_GT_Mini.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef HWDEF_BLF_GT_MINI_H
-#define HWDEF_BLF_GT_MINI_H
-
-/* BLF/Lumintop GT Mini driver layout
- */
-// exactly the same as a D1S, but with a lighted button
-#include "hwdef-Emisar_D1S.h"
-
-// lighted button
-#ifndef AUXLED_PIN
-#define AUXLED_PIN PB4 // pin 3
-#endif
-
-#endif
diff --git a/hwdef-BLF_LT1-t1616.h b/hwdef-BLF_LT1-t1616.h
deleted file mode 100644
index 8e5c58b..0000000
--- a/hwdef-BLF_LT1-t1616.h
+++ /dev/null
@@ -1,107 +0,0 @@
-#ifndef HWDEF_BLF_LANTERN_T1616_H
-#define HWDEF_BLF_LANTERN_T1616_H
-
-/* BLF LT1 driver layout using the Attiny1616
-
-Driver pinout:
- * eSwitch: PA5
- * Aux LED: PB5
- * PWM FET: PB0 (TCA0 WO0)
- * PWM 1x7135: PB1 (TCA0 WO1)
- * Voltage: VCC
-
-*/
-
-
-#define LAYOUT_DEFINED
-
-#ifdef ATTINY
-#undef ATTINY
-#endif
-#define ATTINY 1616
-#include <avr/io.h>
-
-#define PWM_CHANNELS 1
-
-#ifndef SWITCH_PIN
-#define SWITCH_PIN PIN5_bp
-#define SWITCH_PORT VPORTA.IN
-#define SWITCH_ISC_REG PORTA.PIN2CTRL
-#define SWITCH_VECT PORTA_PORT_vect
-#define SWITCH_INTFLG VPORTA.INTFLAGS
-#endif
-
-
-// usually PWM1_LVL would be a hardware register, but we need to abstract
-// it out to a soft brightness value, in order to handle tint ramping
-// (this allows smooth thermal regulation to work, and makes things
-// otherwise simpler and easier)
-uint8_t PWM1_LVL;
-
-// warm tint channel
-#ifndef PWM1_PIN
-#define PWM1_PIN PB1 //
-#define TINT1_LVL TCA0.SINGLE.CMP1 // CMP1 is the output compare register for PB1
-#endif
-
-// cold tint channel
-#ifndef PWM2_PIN
-#define PWM2_PIN PB0 //
-#define TINT2_LVL TCA0.SINGLE.CMP0 // CMP0 is the output compare register for PB0
-#endif
-
-// average drop across diode on this hardware
-#ifndef VOLTAGE_FUDGE_FACTOR
-#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V
-#endif
-
-
-// lighted button
-#ifndef AUXLED_PIN
-#define AUXLED_PIN PIN5_bp
-#define AUXLED_PORT PORTB
-#endif
-
-
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
-inline void hwdef_setup() {
-
- // set up the system clock to run at 5 MHz instead of the default 3.33 MHz
- _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, CLKCTRL_PDIV_4X_gc | CLKCTRL_PEN_bm );
-
- //VPORTA.DIR = ...;
- VPORTB.DIR = PIN0_bm | PIN1_bm | PIN5_bm; // Outputs: Aux LED and PWMs
- //VPORTC.DIR = ...;
-
- // enable pullups on the unused pins to reduce power
- PORTA.PIN0CTRL = PORT_PULLUPEN_bm;
- PORTA.PIN1CTRL = PORT_PULLUPEN_bm;
- PORTA.PIN2CTRL = PORT_PULLUPEN_bm;
- PORTA.PIN3CTRL = PORT_PULLUPEN_bm;
- PORTA.PIN4CTRL = PORT_PULLUPEN_bm;
- PORTA.PIN5CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // eSwitch
- PORTA.PIN6CTRL = PORT_PULLUPEN_bm;
- PORTA.PIN7CTRL = PORT_PULLUPEN_bm;
-
- //PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // cold tint channel
- //PORTB.PIN1CTRL = PORT_PULLUPEN_bm; // warm tint channel
- PORTB.PIN2CTRL = PORT_PULLUPEN_bm;
- PORTB.PIN3CTRL = PORT_PULLUPEN_bm;
- PORTB.PIN4CTRL = PORT_PULLUPEN_bm;
- //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // Aux LED
-
- PORTC.PIN0CTRL = PORT_PULLUPEN_bm;
- PORTC.PIN1CTRL = PORT_PULLUPEN_bm;
- PORTC.PIN2CTRL = PORT_PULLUPEN_bm;
- PORTC.PIN3CTRL = PORT_PULLUPEN_bm;
-
- // set up the PWM
- // TODO: add references to MCU documentation
- TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm | TCA_SINGLE_CMP1EN_bm | TCA_SINGLE_WGMODE_SINGLESLOPE_gc;
- TCA0.SINGLE.PER = 255;
- TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc | TCA_SINGLE_ENABLE_bm;
-}
-
-
-#endif
diff --git a/hwdef-BLF_LT1.h b/hwdef-BLF_LT1.h
deleted file mode 100644
index 4e81c42..0000000
--- a/hwdef-BLF_LT1.h
+++ /dev/null
@@ -1,58 +0,0 @@
-#ifndef HWDEF_BLF_LT1_H
-#define HWDEF_BLF_LT1_H
-
-/* BLF LT1 driver layout
- * ----
- * Reset -|1 8|- VCC
- * eswitch -|2 7|- (unused)
- * aux LED -|3 6|- PWM (5000K)
- * GND -|4 5|- PWM (3000K)
- * ----
- */
-
-#define ATTINY 85
-#include <avr/io.h>
-
-#define PWM_CHANNELS 1 // 1 virtual channel (1 for main LEDs + 1 for 2nd LEDs)
-#define PWM_BITS 9 // 0 to 255 at 15.6 kHz, but goes to 510 for "200%" turbo
-#define PWM_TOP 255
-
-// dynamic PWM with tint ramping (not supported on attiny85)
-//#define USE_DYN_PWM // dynamic frequency and speed
-//#define PWM1_CNT TCNT0 // for dynamic PWM, reset phase
-//#define PWM1_PHASE_RESET_OFF // force reset while shutting off
-//#define PWM1_PHASE_RESET_ON // force reset while turning on
-//#define PWM1_PHASE_SYNC // manual sync while changing level
-
-// usually PWM1_LVL would be a hardware register, but we need to abstract
-// it out to a soft brightness value, in order to handle tint ramping
-// (this allows smooth thermal regulation to work, and makes things
-// otherwise simpler and easier)
-uint16_t PWM1_LVL;
-
-#define PWM1_PIN PB0 // pin 5, warm tint PWM
-#define TINT1_LVL OCR0A // OCR0A is the output compare register for PB0
-
-#define PWM2_PIN PB1 // pin 6, cold tint PWM
-#define TINT2_LVL OCR0B // OCR0B is the output compare register for PB1
-
-
-#define AUXLED_PIN PB4 // pin 3
-
-#define SWITCH_PIN PB3 // pin 2
-#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt
-
-#define ADC_PRSCL 0x07 // clk/128
-
-// average drop across diode on this hardware
-#ifndef VOLTAGE_FUDGE_FACTOR
-#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V
-#endif
-
-#define FAST 0xA3 // fast PWM both channels
-#define PHASE 0xA1 // phase-correct PWM both channels
-
-#define LAYOUT_DEFINED
-
-
-#endif
diff --git a/hwdef-BLF_Q8-T1616.h b/hwdef-BLF_Q8-T1616.h
deleted file mode 100644
index d6ad760..0000000
--- a/hwdef-BLF_Q8-T1616.h
+++ /dev/null
@@ -1,107 +0,0 @@
-#ifndef HWDEF_BLF_Q8_T1616_H
-#define HWDEF_BLF_Q8_T1616_H
-
-/* BLF Q8 driver layout using the Attiny1616
-
-Driver pinout:
- * eSwitch: PA5
- * Aux LED: PB5
- * PWM FET: PB0 (TCA0 WO0)
- * PWM 1x7135: PB1 (TCA0 WO1)
- * Voltage: VCC
-
-*/
-
-
-#define LAYOUT_DEFINED
-
-#ifdef ATTINY
-#undef ATTINY
-#endif
-#define ATTINY 1616
-#include <avr/io.h>
-
-#define PWM_CHANNELS 2
-
-#ifndef SWITCH_PIN
-#define SWITCH_PIN PIN5_bp
-#define SWITCH_PORT VPORTA.IN
-#define SWITCH_ISC_REG PORTA.PIN2CTRL
-#define SWITCH_VECT PORTA_PORT_vect
-#define SWITCH_INTFLG VPORTA.INTFLAGS
-#endif
-
-
-// 7135 channel
-#ifndef PWM1_PIN
-#define PWM1_PIN PB1 //
-#define PWM1_LVL TCA0.SINGLE.CMP1 // CMP1 is the output compare register for PB1
-#endif
-
-// FET channel
-#ifndef PWM2_PIN
-#define PWM2_PIN PB0 //
-#define PWM2_LVL TCA0.SINGLE.CMP0 // CMP0 is the output compare register for PB0
-#endif
-
-// average drop across diode on this hardware
-#ifndef VOLTAGE_FUDGE_FACTOR
-#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V
-#endif
-
-
-// lighted button
-#ifndef AUXLED_PIN
-#define AUXLED_PIN PIN5_bp
-#define AUXLED_PORT PORTB
-#endif
-
-
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
-inline void hwdef_setup() {
-
- // set up the system clock to run at 10 MHz instead of the default 3.33 MHz
- _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm );
-
- //VPORTA.DIR = ...;
- VPORTB.DIR = PIN0_bm | PIN1_bm | PIN5_bm; // Outputs: Aux LED and PWMs
- //VPORTC.DIR = ...;
-
- // enable pullups on the unused pins to reduce power
- PORTA.PIN0CTRL = PORT_PULLUPEN_bm;
- PORTA.PIN1CTRL = PORT_PULLUPEN_bm;
- PORTA.PIN2CTRL = PORT_PULLUPEN_bm;
- PORTA.PIN3CTRL = PORT_PULLUPEN_bm;
- PORTA.PIN4CTRL = PORT_PULLUPEN_bm;
- PORTA.PIN5CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // eSwitch
- PORTA.PIN6CTRL = PORT_PULLUPEN_bm;
- PORTA.PIN7CTRL = PORT_PULLUPEN_bm;
-
- //PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // FET channel
- //PORTB.PIN1CTRL = PORT_PULLUPEN_bm; // 7135 channel
- PORTB.PIN2CTRL = PORT_PULLUPEN_bm;
- PORTB.PIN3CTRL = PORT_PULLUPEN_bm;
- PORTB.PIN4CTRL = PORT_PULLUPEN_bm;
- //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // Aux LED
-
- PORTC.PIN0CTRL = PORT_PULLUPEN_bm;
- PORTC.PIN1CTRL = PORT_PULLUPEN_bm;
- PORTC.PIN2CTRL = PORT_PULLUPEN_bm;
- PORTC.PIN3CTRL = PORT_PULLUPEN_bm;
-
- // set up the PWM
- // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf
- // PB0 is TCA0:WO0, use TCA_SINGLE_CMP0EN_bm
- // PB1 is TCA0:WO1, use TCA_SINGLE_CMP1EN_bm
- // PB2 is TCA0:WO2, use TCA_SINGLE_CMP2EN_bm
- // For Fast (Single Slope) PWM use TCA_SINGLE_WGMODE_SINGLESLOPE_gc
- // For Phase Correct (Dual Slope) PWM use TCA_SINGLE_WGMODE_DSBOTTOM_gc
- // See the manual for other pins, clocks, configs, portmux, etc
- TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm | TCA_SINGLE_CMP1EN_bm | TCA_SINGLE_WGMODE_DSBOTTOM_gc;
- TCA0.SINGLE.PER = 255;
- TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc | TCA_SINGLE_ENABLE_bm;
-}
-
-
-#endif
diff --git a/hwdef-Emisar_D1.h b/hwdef-Emisar_D1.h
deleted file mode 100644
index caf1d4e..0000000
--- a/hwdef-Emisar_D1.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef HWDEF_EMISAR_D1_H
-#define HWDEF_EMISAR_D1_H
-
-/* Emisar D1 driver layout
- */
-// D1 driver is exactly the same as a D4
-#include "hwdef-Emisar_D4.h"
-
-#endif
diff --git a/hwdef-Emisar_D18.h b/hwdef-Emisar_D18.h
deleted file mode 100644
index 3a238eb..0000000
--- a/hwdef-Emisar_D18.h
+++ /dev/null
@@ -1,48 +0,0 @@
-#ifndef HWDEF_EMISAR_D18_H
-#define HWDEF_EMISAR_D18_H
-
-/* Emisar D18 (FET+13+1) driver layout
- * ----
- * Reset -|1 8|- VCC
- * eswitch -|2 7|- aux LED?
- * FET -|3 6|- 13x7135
- * GND -|4 5|- 1x7135
- * ----
- */
-
-#define PWM_CHANNELS 3
-
-#ifndef SWITCH_PIN
-#define SWITCH_PIN PB3 // pin 2
-#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt
-#endif
-
-#ifndef PWM1_PIN
-#define PWM1_PIN PB0 // pin 5, 1x7135 PWM
-#define PWM1_LVL OCR0A // OCR0A is the output compare register for PB0
-#endif
-#ifndef PWM2_PIN
-#define PWM2_PIN PB1 // pin 6, 7x7135 PWM
-#define PWM2_LVL OCR0B // OCR0B is the output compare register for PB1
-#endif
-#ifndef PWM3_PIN
-#define PWM3_PIN PB4 // pin 3, FET PWM
-#define PWM3_LVL OCR1B // OCR1B is the output compare register for PB4
-#endif
-
-#ifndef AUXLED_PIN
-#define AUXLED_PIN PB2 // pin 7
-#endif
-#define ADC_PRSCL 0x07 // clk/128
-
-// average drop across diode on this hardware
-#ifndef VOLTAGE_FUDGE_FACTOR
-#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V
-#endif
-
-#define FAST 0xA3 // fast PWM both channels
-#define PHASE 0xA1 // phase-correct PWM both channels
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-Emisar_D1S.h b/hwdef-Emisar_D1S.h
deleted file mode 100644
index 2e9fffa..0000000
--- a/hwdef-Emisar_D1S.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef HWDEF_EMISAR_D1S_H
-#define HWDEF_EMISAR_D1S_H
-
-/* Emisar D1S driver layout
- */
-// D1S driver is exactly the same as a D4
-#include "hwdef-Emisar_D4.h"
-
-#endif
diff --git a/hwdef-Emisar_D4.h b/hwdef-Emisar_D4.h
deleted file mode 100644
index d062d6f..0000000
--- a/hwdef-Emisar_D4.h
+++ /dev/null
@@ -1,47 +0,0 @@
-#ifndef HWDEF_EMISAR_D4_H
-#define HWDEF_EMISAR_D4_H
-
-/* Emisar D4 driver layout
- * ----
- * Reset -|1 8|- VCC
- * eswitch -|2 7|-
- * AUX LED -|3 6|- PWM (FET)
- * GND -|4 5|- PWM (1x7135)
- * ----
- */
-
-#define LAYOUT_DEFINED
-
-#define PWM_CHANNELS 2
-
-//#define AUXLED_PIN PB4 // pin 3
-
-#ifndef SWITCH_PIN
-#define SWITCH_PIN PB3 // pin 2
-#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt
-#endif
-
-#ifndef PWM1_PIN
-#define PWM1_PIN PB0 // pin 5, 1x7135 PWM
-#define PWM1_LVL OCR0A // OCR0A is the output compare register for PB0
-#endif
-#ifndef PWM2_PIN
-#define PWM2_PIN PB1 // pin 6, FET PWM
-#define PWM2_LVL OCR0B // OCR0B is the output compare register for PB1
-#endif
-
-// (FIXME: remove? not used?)
-//#define VOLTAGE_PIN PB2 // pin 7, voltage ADC
-//#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
-//#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
-#define ADC_PRSCL 0x07 // clk/128
-
-// average drop across diode on this hardware
-#ifndef VOLTAGE_FUDGE_FACTOR
-#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V
-#endif
-
-#define FAST 0xA3 // fast PWM both channels
-#define PHASE 0xA1 // phase-correct PWM both channels
-
-#endif
diff --git a/hwdef-Emisar_D4S.h b/hwdef-Emisar_D4S.h
deleted file mode 100644
index 05a60f9..0000000
--- a/hwdef-Emisar_D4S.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef HWDEF_EMISAR_D4S_H
-#define HWDEF_EMISAR_D4S_H
-
-/* Emisar D4S driver layout
- */
-// same as a D4, basically
-#include "hwdef-Emisar_D4.h"
-
-// ... except the D4S has aux LEDs under the optic
-#ifndef AUXLED_PIN
-#define AUXLED_PIN PB4 // pin 3
-#endif
-
-#endif
diff --git a/hwdef-Emisar_D4Sv2-tintramp.h b/hwdef-Emisar_D4Sv2-tintramp.h
deleted file mode 100644
index 0f4a77a..0000000
--- a/hwdef-Emisar_D4Sv2-tintramp.h
+++ /dev/null
@@ -1,188 +0,0 @@
-#ifndef HWDEF_D4SV2_TINTRAMP_H
-#define HWDEF_D4SV2_TINTRAMP_H
-
-/* Emisar D4Sv2 w/ tint ramping
- * (based on the Noctigon K9.3 driver layout (attiny1634))
- *
- * Pin / Name / Function
- * 1 PA6 2nd LED PWM (linear) (PWM1B)
- * 2 PA5 R: red aux LED (PWM0B)
- * 3 PA4 G: green aux LED
- * 4 PA3 B: blue aux LED
- * 5 PA2 button LED
- * 6 PA1 Opamp 2 enable (2nd LEDs)
- * 7 PA0 Opamp 1 enable (main LEDs)
- * 8 GND GND
- * 9 VCC VCC
- * 10 PC5 (none)
- * 11 PC4 (none)
- * 12 PC3 RESET
- * 13 PC2 (none)
- * 14 PC1 SCK
- * 15 PC0 main LED PWM (FET) (PWM0A) (unused on some models because tint ramping)
- * 16 PB3 main LED PWM (linear) (PWM1A)
- * 17 PB2 MISO
- * 18 PB1 MOSI / battery voltage (ADC6)
- * 19 PB0 (none)
- * 20 PA7 e-switch (PCINT7)
- * ADC12 thermal sensor
- *
- * Main LED power uses one pin to turn the Opamp on/off,
- * and one pin to control Opamp power level.
- * Main brightness control uses the power level pin, with 4 kHz 10-bit PWM.
- * The on/off pin is only used to turn the main LED on and off,
- * not to change brightness.
- */
-
-#ifdef ATTINY
-#undef ATTINY
-#endif
-#define ATTINY 1634
-#include <avr/io.h>
-
-#define PWM_CHANNELS 1 // 1 virtual channel (1 for main LEDs + 1 for 2nd LEDs)
-#define PWM_BITS 14 // 0 to 16383 at variable Hz, not 0 to 255 at 16 kHz
-#define PWM_TOP 511
-// dynamic PWM with tint ramping
-#define USE_DYN_PWM // dynamic frequency and speed
-#define PWM_DATATYPE2 uint32_t // only needs 32-bit if ramp values go over 255
-
-#ifndef SWITCH_PIN
-#define SWITCH_PIN PA7 // pin 20
-#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt
-#define SWITCH_PCIE PCIE0 // PCIE1 is for PCINT[7:0]
-#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]
-#endif
-
-// usually PWM1_LVL would be a hardware register, but we need to abstract
-// it out to a soft brightness value, in order to handle tint ramping
-// (this allows smooth thermal regulation to work, and makes things
-// otherwise simpler and easier)
-uint16_t PWM1_LVL;
-#define PWM1_PIN PB3 // pin 16, Opamp reference
-#define TINT1_LVL OCR1A // OCR1A is the output compare register for PB3
-#define PWM1_CNT TCNT1 // for dynamic PWM, reset phase
-#define PWM1_PHASE_RESET_OFF // force reset while shutting off
-#define PWM1_PHASE_RESET_ON // force reset while turning on
-#define PWM1_PHASE_SYNC // manual sync while changing level
-
-// gah, this driver is weird...
-// two linear channels are treated as one,
-// while there's also a FET on one channel for turbo on half the LEDs
-// so the FET needs to be "PWM2" but the second linear is "TINT2"
-#define PWM3_PIN PA6 // pin 1, 2nd LED Opamp reference
-#define TINT2_LVL OCR1B // OCR1B is the output compare register for PA6
-
-#define PWM2_PIN PC0 // pin 15, DD FET PWM
-#define PWM2_LVL OCR0A // OCR0A is the output compare register for PC0
-
-// PWM parameters of both channels are tied together because they share a counter
-#define PWM1_TOP ICR1 // holds the TOP value for for variable-resolution PWM
-
-#define LED_ENABLE_PIN PA0 // pin 7, Opamp power
-#define LED_ENABLE_PORT PORTA // control port for PA0
-
-#define LED2_ENABLE_PIN PA1 // pin 6, Opamp power
-#define LED2_ENABLE_PORT PORTA // control port for PA1
-
-
-#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
-
-// this light has 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
-#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
-
-#define BUTTON_LED_PIN PA2 // pin 5
-#define BUTTON_LED_PORT PORTA // for all "PA" pins
-#define BUTTON_LED_DDR DDRA // for all "PA" pins
-#define BUTTON_LED_PUE PUEA // for all "PA" pins
-
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
-inline void hwdef_setup() {
- // enable output ports
- DDRC = (1 << PWM2_PIN);
- DDRB = (1 << PWM1_PIN);
- DDRA = (1 << PWM3_PIN)
- | (1 << AUXLED_R_PIN)
- | (1 << AUXLED_G_PIN)
- | (1 << AUXLED_B_PIN)
- | (1 << BUTTON_LED_PIN)
- | (1 << LED_ENABLE_PIN)
- | (1 << LED2_ENABLE_PIN)
- ;
-
- // 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
- // Linear opamp PWM for both main and 2nd LEDs (10-bit)
- // WGM1[3:0]: 1,0,1,0: PWM, Phase Correct, 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) | (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) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5)
- ;
-
- // FET PWM (8-bit; this channel can't do 10-bit)
- // WGM0[2:0]: 0,0,1: PWM, Phase Correct, 8-bit (DS table 11-8)
- // CS0[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 11-9)
- // COM0A[1:0]: 1,0: PWM OC0A in the normal direction (DS table 11-4)
- // COM0B[1:0]: 1,0: PWM OC0B in the normal direction (DS table 11-7)
- TCCR0A = (0<<WGM01) | (1<<WGM00) // 8-bit (TOP=0xFF) (DS table 11-8)
- | (1<<COM0A1) | (0<<COM0A0) // PWM 0A in normal direction (DS table 11-4)
- //| (1<<COM0B1) | (0<<COM0B0) // PWM 0B in normal direction (DS table 11-7)
- ;
- TCCR0B = (0<<CS02) | (0<<CS01) | (1<<CS00) // clk/1 (no prescaling) (DS table 11-9)
- | (0<<WGM02) // phase-correct PWM (DS table 11-8)
- ;
- // set PWM resolution
- PWM1_TOP = PWM_TOP;
-
- // set up e-switch
- SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch
- SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
-}
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-Emisar_D4Sv2.h b/hwdef-Emisar_D4Sv2.h
deleted file mode 100644
index d68b7ac..0000000
--- a/hwdef-Emisar_D4Sv2.h
+++ /dev/null
@@ -1,122 +0,0 @@
-#ifndef HWDEF_EMISAR_D4SV2_H
-#define HWDEF_EMISAR_D4SV2_H
-
-/* Emisar D4Sv2 driver layout (attiny1634)
- * (same layout as D4v2, except it's a FET+3+1 instead of FET+1)
- *
- * Pin / Name / Function
- * 1 PA6 FET PWM (PWM1B)
- * 2 PA5 red aux LED (PWM0B)
- * 3 PA4 green aux LED
- * 4 PA3 blue aux LED
- * 5 PA2 e-switch
- * 6 PA1 (none)
- * 7 PA0 (none)
- * 8 GND GND
- * 9 VCC VCC
- * 10 PC5 (none)
- * 11 PC4 (none)
- * 12 PC3 RESET
- * 13 PC2 (none)
- * 14 PC1 SCK
- * 15 PC0 3x7135 PWM (PWM0A)
- * 16 PB3 1x7135 PWM (PWM1A)
- * 17 PB2 MISO
- * 18 PB1 MOSI
- * 19 PB0 (none)
- * 20 PA7 (none)
- * ADC12 thermal sensor
- */
-
-#ifdef ATTINY
-#undef ATTINY
-#endif
-#define ATTINY 1634
-#include <avr/io.h>
-
-#define SWITCH_PIN PA2 // pin 5
-#define SWITCH_PCINT PCINT2 // pin 5 pin change interrupt
-#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
-#define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0]
-#define SWITCH_PORT PINA // PINA or PINB or PINC
-
-#define PWM_CHANNELS 3
-
-#define PWM1_PIN PB3 // pin 16, 1x7135 PWM
-#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3
-
-#define PWM2_PIN PC0 // pin 15, 3x7135 PWM
-#define PWM2_LVL OCR0A // OCR0A is the output compare register for PC0
-
-#define PWM3_PIN PA6 // pin 1, FET PWM
-#define PWM3_LVL OCR1B // OCR1B is the output compare register for PB1
-
-
-#define ADC_PRSCL 0x07 // clk/128
-
-// average drop across diode on this hardware
-#ifndef VOLTAGE_FUDGE_FACTOR
-#define VOLTAGE_FUDGE_FACTOR 4 // add 0.20V (measured 0.22V)
-#endif
-
-// this light has 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
-#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
-
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
-inline void hwdef_setup() {
- // enable output ports
- // FET, aux R/G/B
- DDRA = (1 << PWM3_PIN)
- | (1 << AUXLED_R_PIN)
- | (1 << AUXLED_G_PIN)
- | (1 << AUXLED_B_PIN)
- ;
- // 1x7135
- DDRB = (1 << PWM1_PIN);
- // 3x7135
- DDRC = (1 << PWM2_PIN);
-
- // 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,0,1: PWM, Phase Correct, 8-bit (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 = (0<<WGM11) | (1<<WGM10) // 8-bit (TOP=0xFF) (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)
- | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5)
- ;
-
- // WGM0[2:0]: 0,0,1: PWM, Phase Correct (DS table 11-8)
- // CS0[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 11-9)
- // COM0A[1:0]: 1,0: PWM OC0A in the normal direction (DS table 11-4)
- // COM0B[1:0]: 0,0: OC0B disabled (DS table 11-7)
- // TCCR0A: COM0A1, COM0A0, COM0B1, COM0B0, -, -, WGM01, WGM00
- TCCR0A = (0<<WGM01) | (1<<WGM00) // PWM, Phase Correct, TOP=0xFF (DS table 11-5)
- | (1<<COM1A1) | (0<<COM1A0) // PWM 0A in normal direction (DS table 11-4)
- | (0<<COM1B1) | (0<<COM1B0) // PWM 0B disabled (DS table 11-7)
- ;
- // TCCR0B: FOC0A, FOC0B, -, -, WGM02, CS02, CS01, CS00
- TCCR0B = (0<<CS02) | (0<<CS01) | (1<<CS00) // clk/1 (no prescaling) (DS table 11-9)
- | (0<<WGM02) // PWM, Phase Correct, TOP=0xFF (DS table 11-8)
- ;
-
- // set up e-switch
- //PORTA = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC?
- PUEA = (1 << SWITCH_PIN); // pull-up for e-switch
- SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
-}
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-Emisar_D4v2.h b/hwdef-Emisar_D4v2.h
deleted file mode 100644
index e118ed5..0000000
--- a/hwdef-Emisar_D4v2.h
+++ /dev/null
@@ -1,104 +0,0 @@
-#ifndef HWDEF_EMISAR_D4V2_H
-#define HWDEF_EMISAR_D4V2_H
-
-/* Emisar D4v2 driver layout (attiny1634)
- *
- * Pin / Name / Function
- * 1 PA6 FET PWM (PWM1B)
- * 2 PA5 red aux LED (PWM0B)
- * 3 PA4 green aux LED
- * 4 PA3 blue aux LED
- * 5 PA2 e-switch
- * 6 PA1 button LED
- * 7 PA0 (none)
- * 8 GND GND
- * 9 VCC VCC
- * 10 PC5 (none)
- * 11 PC4 (none)
- * 12 PC3 RESET
- * 13 PC2 (none)
- * 14 PC1 SCK
- * 15 PC0 (none) PWM0A
- * 16 PB3 7135 PWM (PWM1A)
- * 17 PB2 MISO
- * 18 PB1 MOSI
- * 19 PB0 (none)
- * 20 PA7 (none)
- * ADC12 thermal sensor
- */
-
-#ifdef ATTINY
-#undef ATTINY
-#endif
-#define ATTINY 1634
-#include <avr/io.h>
-
-#define PWM_CHANNELS 2
-
-#define SWITCH_PIN PA2 // pin 5
-#define SWITCH_PCINT PCINT2 // pin 5 pin change interrupt
-#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
-#define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0]
-#define SWITCH_PORT PINA // PINA or PINB or PINC
-
-#define PWM1_PIN PB3 // pin 16, 1x7135 PWM
-#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3
-
-#define PWM2_PIN PA6 // pin 1, FET PWM
-#define PWM2_LVL OCR1B // OCR1B is the output compare register for PB1
-
-
-#define ADC_PRSCL 0x07 // clk/128
-
-// average drop across diode on this hardware
-#ifndef VOLTAGE_FUDGE_FACTOR
-#define VOLTAGE_FUDGE_FACTOR 4 // add 0.20V (measured 0.22V)
-#endif
-
-// this light has 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
-#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
-
-#define BUTTON_LED_PIN PA1 // pin 6
-#define BUTTON_LED_PORT PORTA // for all "PA" pins
-#define BUTTON_LED_DDR DDRA // for all "PA" pins
-#define BUTTON_LED_PUE PUEA // for all "PA" pins
-
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
-inline void hwdef_setup() {
- // enable output ports
- // 7135
- DDRB = (1 << PWM1_PIN);
- // FET, aux R/G/B, button LED
- DDRA = (1 << PWM2_PIN)
- | (1 << AUXLED_R_PIN)
- | (1 << AUXLED_G_PIN)
- | (1 << AUXLED_B_PIN)
- | (1 << BUTTON_LED_PIN)
- ;
-
- // 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
- TCCR1A = (0<<WGM11) | (1<<WGM10) // 8-bit (TOP=0xFF) (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)
- | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5)
- ;
-
- // set up e-switch
- //PORTA = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC?
- PUEA = (1 << SWITCH_PIN); // pull-up for e-switch
- SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
-}
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-FW3A.h b/hwdef-FW3A.h
deleted file mode 100644
index e8875d7..0000000
--- a/hwdef-FW3A.h
+++ /dev/null
@@ -1,50 +0,0 @@
-#ifndef HWDEF_FW3A_H
-#define HWDEF_FW3A_H
-
-/* BLF/TLF FW3A driver layout
- * ----
- * Reset -|1 8|- VCC
- * eswitch -|2 7|- optic nerve
- * FET -|3 6|- 7x7135
- * GND -|4 5|- 1x7135
- * ----
- */
-
-#define PWM_CHANNELS 3
-
-#ifndef SWITCH_PIN
-#define SWITCH_PIN PB3 // pin 2
-#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt
-#endif
-
-#ifndef PWM1_PIN
-#define PWM1_PIN PB0 // pin 5, 1x7135 PWM
-#define PWM1_LVL OCR0A // OCR0A is the output compare register for PB0
-#endif
-#ifndef PWM2_PIN
-#define PWM2_PIN PB1 // pin 6, 7x7135 PWM
-#define PWM2_LVL OCR0B // OCR0B is the output compare register for PB1
-#endif
-#ifndef PWM3_PIN
-#define PWM3_PIN PB4 // pin 3, FET PWM
-#define PWM3_LVL OCR1B // OCR1B is the output compare register for PB4
-#endif
-
-#ifndef VISION_PIN
-#define VISION_PIN PB2 // pin 7, optic nerve
-//#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
-//#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
-#endif
-#define ADC_PRSCL 0x07 // clk/128
-
-// average drop across diode on this hardware
-#ifndef VOLTAGE_FUDGE_FACTOR
-#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V
-#endif
-
-#define FAST 0xA3 // fast PWM both channels
-#define PHASE 0xA1 // phase-correct PWM both channels
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-Mateminco_MF01-Mini.h b/hwdef-Mateminco_MF01-Mini.h
deleted file mode 100644
index c0de533..0000000
--- a/hwdef-Mateminco_MF01-Mini.h
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef HWDEF_MF01_MINI_H
-#define HWDEF_MF01_MINI_H
-
-/* MF01-Mini driver layout
- * ----
- * Reset -|1 8|- VCC
- * eswitch -|2 7|- aux LEDs
- * FET PWM -|3 6|- PWM (7x7135)
- * GND -|4 5|- PWM (1x7135)
- * ----
- */
-
-#define PWM_CHANNELS 3
-
-#ifndef AUXLED_PIN
-#define AUXLED_PIN PB2 // pin 7
-#endif
-
-#ifndef SWITCH_PIN
-#define SWITCH_PIN PB3 // pin 2
-#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt
-#endif
-
-#ifndef PWM1_PIN
-#define PWM1_PIN PB0 // pin 5, 1x7135 PWM
-#define PWM1_LVL OCR0A // OCR0A is the output compare register for PB0
-#endif
-#ifndef PWM2_PIN
-#define PWM2_PIN PB1 // pin 6, 7x7135 PWM
-#define PWM2_LVL OCR0B // OCR0B is the output compare register for PB1
-#endif
-#ifndef PWM3_PIN
-#define PWM3_PIN PB4 // pin 3, FET PWM
-#define PWM3_LVL OCR1B // OCR1B is the output compare register for PB4
-#endif
-
-#define ADC_PRSCL 0x07 // clk/128
-
-// average drop across diode on this hardware
-#ifndef VOLTAGE_FUDGE_FACTOR
-#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V
-#endif
-
-#define FAST 0xA3 // fast PWM both channels
-#define PHASE 0xA1 // phase-correct PWM both channels
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-Mateminco_MF01S.h b/hwdef-Mateminco_MF01S.h
deleted file mode 100644
index 78a64a6..0000000
--- a/hwdef-Mateminco_MF01S.h
+++ /dev/null
@@ -1,60 +0,0 @@
-#ifndef HWDEF_MF01S_H
-#define HWDEF_MF01S_H
-
-/* MF01S driver layout
- * ----
- * Reset -|1 8|- VCC (unused)
- * eswitch -|2 7|- Voltage divider (2S)
- * AUX LED -|3 6|- PWM (FET)
- * GND -|4 5|- PWM (smaller FET)
- * ----
- */
-
-#define PWM_CHANNELS 2
-
-#ifndef AUXLED_PIN
-#define AUXLED_PIN PB4 // pin 3
-#endif
-
-#ifndef SWITCH_PIN
-#define SWITCH_PIN PB3 // pin 2
-#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt
-#endif
-
-#ifndef PWM1_PIN
-#define PWM1_PIN PB0 // pin 5, 1x7135 PWM
-#define PWM1_LVL OCR0A // OCR0A is the output compare register for PB0
-#endif
-#ifndef PWM2_PIN
-#define PWM2_PIN PB1 // pin 6, FET PWM
-#define PWM2_LVL OCR0B // OCR0B is the output compare register for PB1
-#endif
-
-#define USE_VOLTAGE_DIVIDER // use a voltage divider on pin 7, not VCC
-#ifndef VOLTAGE_PIN
-#define VOLTAGE_PIN PB2 // pin 7, voltage ADC
-#define VOLTAGE_CHANNEL 0x01 // MUX 01 corresponds with PB2
-#define VOLTAGE_ADC ADC1D // Digital input disable bit corresponding with PB2
-// inherited from tk-attiny.h
-//#define VOLTAGE_ADC_DIDR DIDR0 // DIDR for ADC1
-// 1.1V reference, left-adjust, ADC1/PB2
-//#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)
-#endif
-#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)
-#endif
-#ifndef ADC_22
-#define ADC_22 (117*4)
-#endif
-
-#define FAST 0xA3 // fast PWM both channels
-#define PHASE 0xA1 // phase-correct PWM both channels
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-Mateminco_MT35-Mini.h b/hwdef-Mateminco_MT35-Mini.h
deleted file mode 100644
index 344f658..0000000
--- a/hwdef-Mateminco_MT35-Mini.h
+++ /dev/null
@@ -1,47 +0,0 @@
-#ifndef HWDEF_MT35_MINI_H
-#define HWDEF_MT35_MINI_H
-
-/* Mateminco MT35-Mini / Astrolux FT03
- * ----
- * Reset -|1 8|- VCC
- * eswitch -|2 7|- Aux LED
- * 1x7135 -|3 6|- NC
- * GND -|4 5|- FET
- * ----
- */
-
-#define PWM_CHANNELS 2
-
-#ifndef SWITCH_PIN
-#define SWITCH_PIN PB3 // pin 2
-#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt
-#endif
-
-#ifndef PWM1_PIN
-#define PWM1_PIN PB4 // pin 3, 1x7135 PWM
-#define PWM1_LVL OCR1B // OCR1B is the output compare register for PB0
-#endif
-
-#ifndef PWM2_PIN
-#define PWM2_PIN PB0 // pin 5, FET PWM
-#define PWM2_LVL OCR0A // OCR0A is the output compare register for PB4
-#endif
-
-#define ADC_PRSCL 0x07 // clk/128
-
-// average drop across diode on this hardware
-#ifndef VOLTAGE_FUDGE_FACTOR
-#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V
-#endif
-
-// lighted button
-#ifndef AUXLED_PIN
-#define AUXLED_PIN PB2 // pin 7
-#endif
-
-#define FAST 0xA3 // fast PWM both channels
-#define PHASE 0xA1 // phase-correct PWM both channels
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-Noctigon_DM11-12V.h b/hwdef-Noctigon_DM11-12V.h
deleted file mode 100644
index a0d9715..0000000
--- a/hwdef-Noctigon_DM11-12V.h
+++ /dev/null
@@ -1,165 +0,0 @@
-#ifndef HWDEF_NOCTIGON_DM11_12V_H
-#define HWDEF_NOCTIGON_DM11_12V_H
-
-/* Noctigon DM11 (12V) driver layout (attiny1634)
- * (based on Noctigon K1)
- *
- * Pin / Name / Function
- * 1 PA6 (none) (PWM1B) (reserved for DD drivers)
- * 2 PA5 R: red aux LED (PWM0B)
- * 3 PA4 G: green aux LED
- * 4 PA3 B: blue aux LED
- * 5 PA2 L: button LED
- * 6 PA1 (none)
- * 7 PA0 (none)
- * 8 GND GND
- * 9 VCC VCC
- * 10 PC5 (none)
- * 11 PC4 (none)
- * 12 PC3 RESET
- * 13 PC2 (none)
- * 14 PC1 SCK
- * 15 PC0 boost PMIC enable (PWM0A not used)
- * 16 PB3 main LED PWM (PWM1A)
- * 17 PB2 MISO / e-switch? (PCINT10)
- * 18 PB1 MOSI / battery voltage (ADC6)
- * 19 PB0 Opamp power
- * 20 PA7 e-switch? (PCINT7)
- * ADC12 thermal sensor
- *
- * Main LED power uses one pin to turn the Opamp on/off,
- * and one pin to control Opamp power level.
- * Linear brightness control uses the power level pin, with dynamic PWM.
- * The on/off pin is only used to turn the main LED on and off,
- * not to change brightness.
- */
-
-#ifdef ATTINY
-#undef ATTINY
-#endif
-#define ATTINY 1634
-#include <avr/io.h>
-
-#define PWM_CHANNELS 1 // can't use DD FET on boost drivers
-#define PWM_BITS 16 // data type needs 16 bits, not 8
-#define PWM_TOP 255 // highest value used in top half of ramp
-#define USE_DYN_PWM // dynamic frequency and speed
-
-//#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 PCINT_vect PCINT1_vect // ISR for PCINT[11:8]
-
-#define SWITCH_PIN PA7 // pin 20
-#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt
-#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
-#define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0]
-#define SWITCH_PORT PINA // PINA or PINB or PINC
-
-#define PWM1_PIN PB3 // pin 16, Opamp reference
-#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3
-#define PWM1_CNT TCNT1 // for dynamic PWM, reset phase
-#define PWM1_PHASE_RESET_OFF // force reset while shutting off
-#define PWM1_PHASE_RESET_ON // force reset while turning on
-#define PWM1_PHASE_SYNC // manual sync while changing level
-
-// PWM parameters of both channels are tied together because they share a counter
-#define PWM1_TOP ICR1 // holds the TOP value for for variable-resolution PWM
-
-#define LED_ENABLE_PIN PB0 // pin 19, Opamp power
-#define LED_ENABLE_PORT PORTB // control port for PB0
-
-#define LED2_ENABLE_PIN PC0 // pin 15, boost PMIC enable
-#define LED2_ENABLE_PORT PORTC // control port for PC0
-
-
-#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
-
-// this light has 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
-#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
-
-#define BUTTON_LED_PIN PA2 // pin 5
-#define BUTTON_LED_PORT PORTA // for all "PA" pins
-#define BUTTON_LED_DDR DDRA // for all "PA" pins
-#define BUTTON_LED_PUE PUEA // for all "PA" pins
-
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
-inline void hwdef_setup() {
- // enable output ports
- // boost PMIC on/off
- DDRC = (1 << LED2_ENABLE_PIN);
- // Opamp level and Opamp on/off
- DDRB = (1 << PWM1_PIN)
- | (1 << LED_ENABLE_PIN);
- // aux R/G/B, button LED
- DDRA = (1 << AUXLED_R_PIN)
- | (1 << AUXLED_G_PIN)
- | (1 << AUXLED_B_PIN)
- | (1 << BUTTON_LED_PIN)
- ;
-
- // 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]: 1,0,1,0: PWM, Phase Correct, 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]: 0,0: PWM OC1B disabled (DS table 12-4)
- 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)
- | (0<<COM1B1) | (0<<COM1B0) // PWM 1B disabled (DS table 12-4)
- ;
- TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10) // clk/1 (no prescaling) (DS table 12-6)
- | (1<<WGM13) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5)
- ;
-
- // set PWM resolution
- PWM1_TOP = PWM_TOP;
-
- // set up e-switch
- ////PORTB = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC?
- //PUEB = (1 << SWITCH_PIN); // pull-up for e-switch
- //PORTA = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC?
- PUEA = (1 << SWITCH_PIN); // pull-up for e-switch
- SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
-}
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-Noctigon_DM11-SBT90.h b/hwdef-Noctigon_DM11-SBT90.h
deleted file mode 100644
index 8d7aa3d..0000000
--- a/hwdef-Noctigon_DM11-SBT90.h
+++ /dev/null
@@ -1,157 +0,0 @@
-#ifndef HWDEF_NOCTIGON_DM11SBT90_H
-#define HWDEF_NOCTIGON_DM11SBT90_H
-
-/* Noctigon DM11-SBT90.2 driver layout (attiny1634)
- * (based on Noctigon K1 and DM11)
- *
- * Pin / Name / Function
- * 1 PA6 FET PWM (direct drive) (PWM1B)
- * 2 PA5 R: red aux LED (PWM0B)
- * 3 PA4 G: green aux LED
- * 4 PA3 B: blue aux LED
- * 5 PA2 L: button LED
- * 6 PA1 (none)
- * 7 PA0 (none)
- * 8 GND GND
- * 9 VCC VCC
- * 10 PC5 (none)
- * 11 PC4 (none)
- * 12 PC3 RESET
- * 13 PC2 (none)
- * 14 PC1 SCK
- * 15 PC0 (none) PWM0A
- * 16 PB3 main LED PWM (PWM1A)
- * 17 PB2 MISO / e-switch (PCINT10)
- * 18 PB1 MOSI / battery voltage (ADC6)
- * 19 PB0 Opamp power
- * 20 PA7 (none) (PCINT7)
- * ADC12 thermal sensor
- *
- * Main LED power uses one pin to turn the Opamp on/off,
- * and one pin to control Opamp power level.
- * Linear brightness control uses the power level pin, with dynamic PWM.
- * The on/off pin is only used to turn the main LED on and off,
- * not to change brightness.
- * Also has a direct-drive FET for turbo.
- */
-
-#ifdef ATTINY
-#undef ATTINY
-#endif
-#define ATTINY 1634
-#include <avr/io.h>
-
-#define PWM_CHANNELS 2 // override this for the no-FET version
-#define PWM_BITS 16 // data type needs 16 bits, not 8
-#define PWM_TOP 255 // highest value used in top half of ramp
-#define USE_DYN_PWM // dynamic frequency and speed
-
-#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 PCINT_vect PCINT1_vect // ISR for PCINT[11:8]
-
-#define PWM1_PIN PB3 // pin 16, Opamp reference
-#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3
-#define PWM1_CNT TCNT1 // for dynamic PWM, reset phase
-#define PWM1_PHASE_RESET_OFF // force reset while shutting off
-#define PWM1_PHASE_RESET_ON // force reset while turning on
-#define PWM1_PHASE_SYNC // manual sync while changing level
-
-#define PWM2_PIN PA6 // pin 1, DD FET PWM
-#define PWM2_LVL OCR1B // OCR1B is the output compare register for PA6
-
-// PWM parameters of both channels are tied together because they share a counter
-#define PWM1_TOP ICR1 // holds the TOP value for for variable-resolution PWM
-
-#define LED_ENABLE_PIN PB0 // pin 19, Opamp power
-#define LED_ENABLE_PORT PORTB // control port for PB0
-
-
-#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
-
-// this light has 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
-#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
-
-#define BUTTON_LED_PIN PA2 // pin 5
-#define BUTTON_LED_PORT PORTA // for all "PA" pins
-#define BUTTON_LED_DDR DDRA // for all "PA" pins
-#define BUTTON_LED_PUE PUEA // for all "PA" pins
-
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
-inline void hwdef_setup() {
- // enable output ports
- // Opamp level and Opamp on/off
- DDRB = (1 << PWM1_PIN)
- | (1 << LED_ENABLE_PIN);
- // DD FET PWM, aux R/G/B, button LED
- DDRA = (1 << PWM2_PIN)
- | (1 << AUXLED_R_PIN)
- | (1 << AUXLED_G_PIN)
- | (1 << AUXLED_B_PIN)
- | (1 << BUTTON_LED_PIN)
- ;
-
- // 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]: 1,0,1,0: PWM, Phase Correct, 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) | (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) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5)
- ;
-
- // set PWM resolution
- PWM1_TOP = PWM_TOP;
-
- // set up e-switch
- //PORTB = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC?
- PUEB = (1 << SWITCH_PIN); // pull-up for e-switch
- SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
-}
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-Noctigon_DM11.h b/hwdef-Noctigon_DM11.h
deleted file mode 100644
index ea51432..0000000
--- a/hwdef-Noctigon_DM11.h
+++ /dev/null
@@ -1,156 +0,0 @@
-#ifndef HWDEF_NOCTIGON_DM11_H
-#define HWDEF_NOCTIGON_DM11_H
-
-/* Noctigon DM11 driver layout (attiny1634)
- * (based on Noctigon K1)
- *
- * Pin / Name / Function
- * 1 PA6 FET PWM (direct drive) (PWM1B) (on some models)
- * 2 PA5 R: red aux LED (PWM0B)
- * 3 PA4 G: green aux LED
- * 4 PA3 B: blue aux LED
- * 5 PA2 L: button LED
- * 6 PA1 (none)
- * 7 PA0 (none)
- * 8 GND GND
- * 9 VCC VCC
- * 10 PC5 (none)
- * 11 PC4 (none)
- * 12 PC3 RESET
- * 13 PC2 (none)
- * 14 PC1 SCK
- * 15 PC0 (none) PWM0A
- * 16 PB3 main LED PWM (PWM1A)
- * 17 PB2 MISO / (none) (PCINT10)
- * 18 PB1 MOSI / battery voltage (ADC6)
- * 19 PB0 Opamp power
- * 20 PA7 e-switch (PCINT7)
- * ADC12 thermal sensor
- *
- * Main LED power uses one pin to turn the Opamp on/off,
- * and one pin to control Opamp power level.
- * Linear brightness control uses the power level pin, with dynamic PWM.
- * The on/off pin is only used to turn the main LED on and off,
- * not to change brightness.
- * Some models also have a direct-drive FET for turbo.
- */
-
-#ifdef ATTINY
-#undef ATTINY
-#endif
-#define ATTINY 1634
-#include <avr/io.h>
-
-#define PWM_CHANNELS 2 // override this for the no-FET version
-#define PWM_BITS 16 // data type needs 16 bits, not 8
-#define PWM_TOP 255 // highest value used in top half of ramp
-#define USE_DYN_PWM // dynamic frequency and speed
-
-#define SWITCH_PIN PA7 // pin 20
-#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt
-#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
-#define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0]
-#define SWITCH_PORT PINA // PINA or PINB or PINC
-
-#define PWM1_PIN PB3 // pin 16, Opamp reference
-#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3
-#define PWM1_CNT TCNT1 // for dynamic PWM, reset phase
-#define PWM1_PHASE_RESET_OFF // force reset while shutting off
-#define PWM1_PHASE_RESET_ON // force reset while turning on
-#define PWM1_PHASE_SYNC // manual sync while changing level
-
-#define PWM2_PIN PA6 // pin 1, DD FET PWM
-#define PWM2_LVL OCR1B // OCR1B is the output compare register for PA6
-
-// PWM parameters of both channels are tied together because they share a counter
-#define PWM1_TOP ICR1 // holds the TOP value for for variable-resolution PWM
-
-#define LED_ENABLE_PIN PB0 // pin 19, Opamp power
-#define LED_ENABLE_PORT PORTB // control port for PB0
-
-
-#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
-
-// this light has 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
-#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
-
-#define BUTTON_LED_PIN PA2 // pin 5
-#define BUTTON_LED_PORT PORTA // for all "PA" pins
-#define BUTTON_LED_DDR DDRA // for all "PA" pins
-#define BUTTON_LED_PUE PUEA // for all "PA" pins
-
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
-inline void hwdef_setup() {
- // enable output ports
- // Opamp level and Opamp on/off
- DDRB = (1 << PWM1_PIN)
- | (1 << LED_ENABLE_PIN);
- // DD FET PWM, aux R/G/B, button LED
- DDRA = (1 << PWM2_PIN)
- | (1 << AUXLED_R_PIN)
- | (1 << AUXLED_G_PIN)
- | (1 << AUXLED_B_PIN)
- | (1 << BUTTON_LED_PIN)
- ;
-
- // 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]: 1,0,1,0: PWM, Phase Correct, 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) | (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) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5)
- ;
-
- // set PWM resolution
- PWM1_TOP = PWM_TOP;
-
- // set up e-switch
- //PORTA = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC?
- PUEA = (1 << SWITCH_PIN); // pull-up for e-switch
- SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
-}
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-Noctigon_K1-12V.h b/hwdef-Noctigon_K1-12V.h
deleted file mode 100644
index b17aad0..0000000
--- a/hwdef-Noctigon_K1-12V.h
+++ /dev/null
@@ -1,138 +0,0 @@
-#ifndef HWDEF_NOCTIGON_K1_12V_H
-#define HWDEF_NOCTIGON_K1_12V_H
-
-/* Noctigon K1 driver layout (attiny1634)
- * (originally known as Emisar D1S V2)
- *
- * Pin / Name / Function
- * 1 PA6 (none) (PWM1B) (reserved for DD drivers)
- * 2 PA5 R: red aux LED (PWM0B)
- * 3 PA4 G: green aux LED
- * 4 PA3 B: blue aux LED
- * 5 PA2 (none) (reserved for L: button LED (on some models))
- * 6 PA1 (none)
- * 7 PA0 (none)
- * 8 GND GND
- * 9 VCC VCC
- * 10 PC5 (none)
- * 11 PC4 (none)
- * 12 PC3 RESET
- * 13 PC2 (none)
- * 14 PC1 SCK
- * 15 PC0 boost PMIC enable (PWM0A not used)
- * 16 PB3 main LED PWM (PWM1A)
- * 17 PB2 MISO
- * 18 PB1 MOSI / battery voltage (ADC6)
- * 19 PB0 Opamp power
- * 20 PA7 e-switch (PCINT7)
- * ADC12 thermal sensor
- *
- * Main LED power uses one pin to turn the Opamp on/off,
- * and one pin to control Opamp power level.
- * All brightness control uses the power level pin, with 4 kHz 10-bit PWM.
- * The on/off pin is only used to turn the main LED on and off,
- * not to change brightness.
- */
-
-#ifdef ATTINY
-#undef ATTINY
-#endif
-#define ATTINY 1634
-#include <avr/io.h>
-
-#define PWM_CHANNELS 1
-#define PWM_BITS 10 // 0 to 1023 at 4 kHz, not 0 to 255 at 16 kHz
-#define PWM_TOP 1023
-
-#define SWITCH_PIN PA7 // pin 20
-#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt
-#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
-#define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0]
-#define SWITCH_PORT PINA // PINA or PINB or PINC
-
-#define PWM1_PIN PB3 // pin 16, Opamp reference
-#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3
-
-#define LED_ENABLE_PIN PB0 // pin 19, Opamp power
-#define LED_ENABLE_PORT PORTB // control port for PB0
-
-#define LED2_ENABLE_PIN PC0 // pin 15, boost PMIC enable
-#define LED2_ENABLE_PORT PORTC // control port for PC0
-
-
-#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) * 256 / 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
-
-// this light has 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
-#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
-
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
-inline void hwdef_setup() {
- // enable output ports
- // boost PMIC on/off
- DDRC = (1 << LED2_ENABLE_PIN);
- // Opamp level and Opamp on/off
- DDRB = (1 << PWM1_PIN)
- | (1 << LED_ENABLE_PIN);
- // aux R/G/B
- DDRA = (1 << AUXLED_R_PIN)
- | (1 << AUXLED_G_PIN)
- | (1 << AUXLED_B_PIN)
- ;
-
- // 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)
- // 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]: 0,0: PWM OC1B disabled (DS table 12-4)
- TCCR1A = (1<<WGM11) | (1<<WGM10) // 10-bit (TOP=0x03FF) (DS table 12-5)
- | (1<<COM1A1) | (0<<COM1A0) // PWM 1A in normal direction (DS table 12-4)
- | (0<<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)
- | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5)
- ;
-
- // set up e-switch
- PUEA = (1 << SWITCH_PIN); // pull-up for e-switch
- SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
-}
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-Noctigon_K1-SBT90.h b/hwdef-Noctigon_K1-SBT90.h
deleted file mode 100644
index c19a4a6..0000000
--- a/hwdef-Noctigon_K1-SBT90.h
+++ /dev/null
@@ -1,143 +0,0 @@
-#ifndef HWDEF_NOCTIGON_K1SBT90_H
-#define HWDEF_NOCTIGON_K1SBT90_H
-
-/* Noctigon K1-SBT90.2 driver layout (attiny1634)
- * (mostly the same as KR4 driver)
- *
- * Pin / Name / Function
- * 1 PA6 FET PWM (direct drive) (PWM1B)
- * 2 PA5 R: red aux LED (PWM0B)
- * 3 PA4 G: green aux LED
- * 4 PA3 B: blue aux LED
- * 5 PA2 (none)
- * 6 PA1 (none)
- * 7 PA0 (none)
- * 8 GND GND
- * 9 VCC VCC
- * 10 PC5 (none)
- * 11 PC4 (none)
- * 12 PC3 RESET
- * 13 PC2 (none)
- * 14 PC1 SCK
- * 15 PC0 (none) PWM0A
- * 16 PB3 main LED PWM (linear) (PWM1A)
- * 17 PB2 MISO / e-switch (PCINT10)
- * 18 PB1 MOSI / battery voltage (ADC6)
- * 19 PB0 Opamp power
- * 20 PA7 (none)
- * ADC12 thermal sensor
- *
- * Main LED power uses one pin to turn the Opamp on/off,
- * and one pin to control Opamp power level.
- * Main brightness control uses the power level pin, with 4 kHz 10-bit PWM.
- * The on/off pin is only used to turn the main LED on and off,
- * not to change brightness.
- * Also has a direct-drive FET for turbo.
- */
-
-#ifdef ATTINY
-#undef ATTINY
-#endif
-#define ATTINY 1634
-#include <avr/io.h>
-
-#define PWM_CHANNELS 2
-#define PWM_BITS 10 // 0 to 1023 at 4 kHz, not 0 to 255 at 16 kHz
-#define PWM_TOP 1023
-
-#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 PCINT_vect PCINT1_vect // ISR for PCINT[11:8]
-
-#define PWM1_PIN PB3 // pin 16, Opamp reference
-#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3
-
-#define PWM2_PIN PA6 // pin 1, DD FET PWM
-#define PWM2_LVL OCR1B // OCR1B is the output compare register for PA6
-
-#define LED_ENABLE_PIN PB0 // pin 19, Opamp power
-#define LED_ENABLE_PORT PORTB // control port for PB0
-
-
-#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) * 256 / 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 TEMP_CHANNEL 0b00001111
-
-// this light has 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
-#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
-
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
-inline void hwdef_setup() {
- // enable output ports
- // Opamp level and Opamp on/off
- DDRB = (1 << PWM1_PIN)
- | (1 << LED_ENABLE_PIN);
- // DD FET PWM, aux R/G/B
- DDRA = (1 << PWM2_PIN)
- | (1 << AUXLED_R_PIN)
- | (1 << AUXLED_G_PIN)
- | (1 << AUXLED_B_PIN)
- ;
-
- // 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)
- // 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 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)
- | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5)
- ;
-
- // set up e-switch
- //PORTB = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC?
- PUEB = (1 << SWITCH_PIN); // pull-up for e-switch
- SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
-}
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-Noctigon_K1.h b/hwdef-Noctigon_K1.h
deleted file mode 100644
index 4c0ce05..0000000
--- a/hwdef-Noctigon_K1.h
+++ /dev/null
@@ -1,134 +0,0 @@
-#ifndef HWDEF_NOCTIGON_K1_H
-#define HWDEF_NOCTIGON_K1_H
-
-/* Noctigon K1 driver layout (attiny1634)
- * (originally known as Emisar D1S V2)
- *
- * Pin / Name / Function
- * 1 PA6 (none) (PWM1B) (reserved for DD drivers)
- * 2 PA5 R: red aux LED (PWM0B)
- * 3 PA4 G: green aux LED
- * 4 PA3 B: blue aux LED
- * 5 PA2 (none) (reserved for L: button LED (on some models))
- * 6 PA1 (none)
- * 7 PA0 (none)
- * 8 GND GND
- * 9 VCC VCC
- * 10 PC5 (none)
- * 11 PC4 (none)
- * 12 PC3 RESET
- * 13 PC2 (none)
- * 14 PC1 SCK
- * 15 PC0 (none) PWM0A
- * 16 PB3 main LED PWM (PWM1A)
- * 17 PB2 MISO
- * 18 PB1 MOSI / battery voltage (ADC6)
- * 19 PB0 Opamp power
- * 20 PA7 e-switch (PCINT7)
- * ADC12 thermal sensor
- *
- * Main LED power uses one pin to turn the Opamp on/off,
- * and one pin to control Opamp power level.
- * All brightness control uses the power level pin, with 4 kHz 10-bit PWM.
- * The on/off pin is only used to turn the main LED on and off,
- * not to change brightness.
- */
-
-#ifdef ATTINY
-#undef ATTINY
-#endif
-#define ATTINY 1634
-#include <avr/io.h>
-
-#define PWM_CHANNELS 1
-#define PWM_BITS 10 // 0 to 1023 at 4 kHz, not 0 to 255 at 16 kHz
-#define PWM_TOP 1023
-
-#define SWITCH_PIN PA7 // pin 20
-#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt
-#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
-#define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0]
-#define SWITCH_PORT PINA // PINA or PINB or PINC
-
-#define PWM1_PIN PB3 // pin 16, Opamp reference
-#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3
-
-#define LED_ENABLE_PIN PB0 // pin 19, Opamp power
-#define LED_ENABLE_PORT PORTB // control port for PB0
-
-
-#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) * 256 / 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
-
-// this light has 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
-#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
-
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
-inline void hwdef_setup() {
- // enable output ports
- // Opamp level and Opamp on/off
- DDRB = (1 << PWM1_PIN)
- | (1 << LED_ENABLE_PIN);
- // aux R/G/B
- DDRA = (1 << AUXLED_R_PIN)
- | (1 << AUXLED_G_PIN)
- | (1 << AUXLED_B_PIN)
- ;
-
- // 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)
- // 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]: 0,0: PWM OC1B disabled (DS table 12-4)
- TCCR1A = (1<<WGM11) | (1<<WGM10) // 10-bit (TOP=0x03FF) (DS table 12-5)
- | (1<<COM1A1) | (0<<COM1A0) // PWM 1A in normal direction (DS table 12-4)
- | (0<<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)
- | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5)
- ;
-
- // set up e-switch
- //PORTA = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC?
- PUEA = (1 << SWITCH_PIN); // pull-up for e-switch
- SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
-}
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-Noctigon_K9.3.h b/hwdef-Noctigon_K9.3.h
deleted file mode 100644
index 6032b4c..0000000
--- a/hwdef-Noctigon_K9.3.h
+++ /dev/null
@@ -1,164 +0,0 @@
-#ifndef HWDEF_NOCTIGON_K93_H
-#define HWDEF_NOCTIGON_K93_H
-
-/* Noctigon K9.3 driver layout (attiny1634)
- *
- * Pin / Name / Function
- * 1 PA6 2nd LED PWM (linear) (PWM1B)
- * 2 PA5 R: red aux LED (PWM0B)
- * 3 PA4 G: green aux LED
- * 4 PA3 B: blue aux LED
- * 5 PA2 button LED
- * 6 PA1 Opamp 2 enable (2nd LEDs)
- * 7 PA0 Opamp 1 enable (main LEDs)
- * 8 GND GND
- * 9 VCC VCC
- * 10 PC5 (none)
- * 11 PC4 (none)
- * 12 PC3 RESET
- * 13 PC2 (none)
- * 14 PC1 SCK
- * 15 PC0 main LED PWM (FET) (PWM0A)
- * 16 PB3 main LED PWM (linear) (PWM1A)
- * 17 PB2 MISO
- * 18 PB1 MOSI / battery voltage (ADC6)
- * 19 PB0 (none)
- * 20 PA7 e-switch (PCINT7)
- * ADC12 thermal sensor
- *
- * Main LED power uses one pin to turn the Opamp on/off,
- * and one pin to control Opamp power level.
- * Main brightness control uses the power level pin, with 4 kHz 10-bit PWM.
- * The on/off pin is only used to turn the main LED on and off,
- * not to change brightness.
- * Some models also have a direct-drive FET for turbo.
- */
-
-#ifdef ATTINY
-#undef ATTINY
-#endif
-#define ATTINY 1634
-#include <avr/io.h>
-
-#define PWM_CHANNELS 3 // 2 for main LEDs, 1 for 2nd LEDs
-#define PWM_BITS 10 // 0 to 1023 at 4 kHz, not 0 to 255 at 16 kHz
-#define PWM_TOP 1023
-
-#define SWITCH_PIN PA7 // pin 20
-#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt
-#define SWITCH_PCIE PCIE0 // PCIE1 is for PCINT[7:0]
-#define SWITCH_PCMSK PCMSK0 // PCMSK1 is for PCINT[7:0]
-#define SWITCH_PORT PINA // PINA or PINB or PINC
-#define PCINT_vect PCINT0_vect // ISR for PCINT[7:0]
-
-#define PWM1_PIN PB3 // pin 16, Opamp reference
-#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3
-
-#define PWM2_PIN PC0 // pin 15, DD FET PWM
-#define PWM2_LVL OCR0A // OCR0A is the output compare register for PC0
-
-#define PWM3_PIN PA6 // pin 1, 2nd LED Opamp reference
-#define PWM3_LVL OCR1B // OCR1B is the output compare register for PA6
-
-#define LED_ENABLE_PIN PA0 // pin 7, Opamp power
-#define LED_ENABLE_PORT PORTA // control port for PA0
-
-#define LED2_ENABLE_PIN PA1 // pin 6, Opamp power
-#define LED2_ENABLE_PORT PORTA // control port for PA1
-
-
-#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
-
-// this light has 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
-#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
-
-#define BUTTON_LED_PIN PA2 // pin 5
-#define BUTTON_LED_PORT PORTA // for all "PA" pins
-#define BUTTON_LED_DDR DDRA // for all "PA" pins
-#define BUTTON_LED_PUE PUEA // for all "PA" pins
-
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
-inline void hwdef_setup() {
- // enable output ports
- DDRC = (1 << PWM2_PIN);
- DDRB = (1 << PWM1_PIN);
- DDRA = (1 << PWM3_PIN)
- | (1 << AUXLED_R_PIN)
- | (1 << AUXLED_G_PIN)
- | (1 << AUXLED_B_PIN)
- | (1 << BUTTON_LED_PIN)
- | (1 << LED_ENABLE_PIN)
- | (1 << LED2_ENABLE_PIN)
- ;
-
- // 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
- // Linear opamp PWM for both main and 2nd LEDs (10-bit)
- // WGM1[3:0]: 0,0,1,1: PWM, Phase Correct, 10-bit (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 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)
- | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5)
- ;
-
- // FET PWM (8-bit; this channel can't do 10-bit)
- // WGM0[2:0]: 0,0,1: PWM, Phase Correct, 8-bit (DS table 11-8)
- // CS0[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 11-9)
- // COM0A[1:0]: 1,0: PWM OC0A in the normal direction (DS table 11-4)
- // COM0B[1:0]: 1,0: PWM OC0B in the normal direction (DS table 11-7)
- TCCR0A = (0<<WGM01) | (1<<WGM00) // 8-bit (TOP=0xFF) (DS table 11-8)
- | (1<<COM0A1) | (0<<COM0A0) // PWM 0A in normal direction (DS table 11-4)
- //| (1<<COM0B1) | (0<<COM0B0) // PWM 0B in normal direction (DS table 11-7)
- ;
- TCCR0B = (0<<CS02) | (0<<CS01) | (1<<CS00) // clk/1 (no prescaling) (DS table 11-9)
- | (0<<WGM02) // phase-correct PWM (DS table 11-8)
- ;
-
- // set up e-switch
- PUEA = (1 << SWITCH_PIN); // pull-up for e-switch
- SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
-}
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-Noctigon_KR4-12V.h b/hwdef-Noctigon_KR4-12V.h
deleted file mode 100644
index e6cf18a..0000000
--- a/hwdef-Noctigon_KR4-12V.h
+++ /dev/null
@@ -1,157 +0,0 @@
-#ifndef HWDEF_NOCTIGON_KR4_12V_H
-#define HWDEF_NOCTIGON_KR4_12V_H
-
-/* Noctigon KR4 (12V) driver layout (attiny1634)
- * (based on Noctigon DM11-12V and KR4)
- *
- * Pin / Name / Function
- * 1 PA6 (none) (PWM1B) (reserved for DD drivers)
- * 2 PA5 R: red aux LED (PWM0B)
- * 3 PA4 G: green aux LED
- * 4 PA3 B: blue aux LED
- * 5 PA2 L: button LED
- * 6 PA1 (none)
- * 7 PA0 (none)
- * 8 GND GND
- * 9 VCC VCC
- * 10 PC5 (none)
- * 11 PC4 (none)
- * 12 PC3 RESET
- * 13 PC2 (none)
- * 14 PC1 SCK
- * 15 PC0 boost PMIC enable (PWM0A not used)
- * 16 PB3 main LED PWM (PWM1A)
- * 17 PB2 MISO / e-switch (PCINT10)
- * 18 PB1 MOSI / battery voltage (ADC6)
- * 19 PB0 Opamp power
- * 20 PA7 (none) (PCINT7)
- * ADC12 thermal sensor
- *
- * Main LED power uses one pin to turn the Opamp on/off,
- * and one pin to control Opamp power level.
- * Linear brightness control uses the power level pin, with dynamic PWM.
- * The on/off pin is only used to turn the main LED on and off,
- * not to change brightness.
- */
-
-#ifdef ATTINY
-#undef ATTINY
-#endif
-#define ATTINY 1634
-#include <avr/io.h>
-
-#define PWM_CHANNELS 1 // can't use DD FET on boost drivers
-#define PWM_BITS 16 // data type needs 16 bits, not 8
-#define PWM_TOP 255 // highest value used in top half of ramp
-#define USE_DYN_PWM // dynamic frequency and speed
-
-#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 PCINT_vect PCINT1_vect // ISR for PCINT[11:8]
-
-#define PWM1_PIN PB3 // pin 16, Opamp reference
-#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3
-#define PWM1_CNT TCNT1 // for dynamic PWM, reset phase
-#define PWM1_PHASE_RESET_OFF // force reset while shutting off
-#define PWM1_PHASE_RESET_ON // force reset while turning on
-#define PWM1_PHASE_SYNC // manual sync while changing level
-
-// PWM parameters of both channels are tied together because they share a counter
-#define PWM1_TOP ICR1 // holds the TOP value for for variable-resolution PWM
-
-#define LED_ENABLE_PIN PB0 // pin 19, Opamp power
-#define LED_ENABLE_PORT PORTB // control port for PB0
-
-#define LED2_ENABLE_PIN PC0 // pin 15, boost PMIC enable
-#define LED2_ENABLE_PORT PORTC // control port for PC0
-
-
-#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
-
-// this light has 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
-#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
-
-#define BUTTON_LED_PIN PA2 // pin 5
-#define BUTTON_LED_PORT PORTA // for all "PA" pins
-#define BUTTON_LED_DDR DDRA // for all "PA" pins
-#define BUTTON_LED_PUE PUEA // for all "PA" pins
-
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
-inline void hwdef_setup() {
- // enable output ports
- // boost PMIC on/off
- DDRC = (1 << LED2_ENABLE_PIN);
- // Opamp level and Opamp on/off
- DDRB = (1 << PWM1_PIN)
- | (1 << LED_ENABLE_PIN);
- // aux R/G/B, button LED
- DDRA = (1 << AUXLED_R_PIN)
- | (1 << AUXLED_G_PIN)
- | (1 << AUXLED_B_PIN)
- | (1 << BUTTON_LED_PIN)
- ;
-
- // 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]: 1,0,1,0: PWM, Phase Correct, 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]: 0,0: PWM OC1B disabled (DS table 12-4)
- 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)
- | (0<<COM1B1) | (0<<COM1B0) // PWM 1B disabled (DS table 12-4)
- ;
- TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10) // clk/1 (no prescaling) (DS table 12-6)
- | (1<<WGM13) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5)
- ;
-
- // set PWM resolution
- PWM1_TOP = PWM_TOP;
-
- // set up e-switch
- //PORTB = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC?
- PUEB = (1 << SWITCH_PIN); // pull-up for e-switch
- SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
-}
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-Noctigon_KR4.h b/hwdef-Noctigon_KR4.h
deleted file mode 100644
index 487d3ac..0000000
--- a/hwdef-Noctigon_KR4.h
+++ /dev/null
@@ -1,161 +0,0 @@
-#ifndef HWDEF_NOCTIGON_KR4_H
-#define HWDEF_NOCTIGON_KR4_H
-
-/* Noctigon KR4 / D4V2.5 driver layout (attiny1634)
- *
- * Pin / Name / Function
- * 1 PA6 FET PWM (direct drive) (PWM1B)
- * 2 PA5 R: red aux LED (PWM0B)
- * 3 PA4 G: green aux LED
- * 4 PA3 B: blue aux LED
- * 5 PA2 button LED (D4V2.5 only)
- * 6 PA1 (none)
- * 7 PA0 (none)
- * 8 GND GND
- * 9 VCC VCC
- * 10 PC5 (none)
- * 11 PC4 (none)
- * 12 PC3 RESET
- * 13 PC2 (none)
- * 14 PC1 SCK
- * 15 PC0 (none) PWM0A
- * 16 PB3 main LED PWM (linear) (PWM1A)
- * 17 PB2 MISO / e-switch (PCINT10)
- * 18 PB1 MOSI / battery voltage (ADC6)
- * 19 PB0 Opamp power
- * 20 PA7 (none)
- * ADC12 thermal sensor
- *
- * Main LED power uses one pin to turn the Opamp on/off,
- * and one pin to control Opamp power level.
- * Main brightness control uses the power level pin, with 4 kHz 10-bit PWM.
- * The on/off pin is only used to turn the main LED on and off,
- * not to change brightness.
- * Some models also have a direct-drive FET for turbo.
- */
-
-#ifdef ATTINY
-#undef ATTINY
-#endif
-#define ATTINY 1634
-#include <avr/io.h>
-
-#define PWM_CHANNELS 2
-#define PWM_BITS 16 // data type needs 16 bits, not 8
-#define PWM_TOP 255 // highest value used in top half of ramp
-#define USE_DYN_PWM // dynamic frequency and speed
-
-#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 PCINT_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 PWM1_PIN PB3 // pin 16, Opamp reference
-#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3
-#define PWM1_CNT TCNT1 // for dynamic PWM, reset phase
-#define PWM1_PHASE_RESET_OFF // force reset while shutting off
-#define PWM1_PHASE_RESET_ON // force reset while turning on
-#define PWM1_PHASE_SYNC // manual sync while changing level
-
-#define PWM2_PIN PA6 // pin 1, DD FET PWM
-#define PWM2_LVL OCR1B // OCR1B is the output compare register for PA6
-
-// PWM parameters of both channels are tied together because they share a counter
-#define PWM1_TOP ICR1 // holds the TOP value for for variable-resolution PWM
-
-#define LED_ENABLE_PIN PB0 // pin 19, Opamp power
-#define LED_ENABLE_PORT PORTB // control port for PB0
-
-
-#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
-
-// this light has 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
-#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
-
-#define BUTTON_LED_PIN PA2 // pin 5
-#define BUTTON_LED_PORT PORTA // for all "PA" pins
-#define BUTTON_LED_DDR DDRA // for all "PA" pins
-#define BUTTON_LED_PUE PUEA // for all "PA" pins
-
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
-inline void hwdef_setup() {
- // enable output ports
- // Opamp level and Opamp on/off
- DDRB = (1 << PWM1_PIN)
- | (1 << LED_ENABLE_PIN);
- // DD FET PWM, aux R/G/B, button LED
- DDRA = (1 << PWM2_PIN)
- | (1 << AUXLED_R_PIN)
- | (1 << AUXLED_G_PIN)
- | (1 << AUXLED_B_PIN)
- | (1 << BUTTON_LED_PIN)
- ;
-
- // 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]: 1,0,1,0: PWM, Phase Correct, 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) | (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) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5)
- ;
-
- // set PWM resolution
- PWM1_TOP = PWM_TOP;
-
- // set up e-switch
- //PORTB = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC?
- PUEB = (1 << SWITCH_PIN); // pull-up for e-switch
- SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
-}
-
-#define LAYOUT_DEFINED
-
-#endif
diff --git a/hwdef-TK_Saber.h b/hwdef-TK_Saber.h
index e6476b8..8ef58d6 100644
--- a/hwdef-TK_Saber.h
+++ b/hwdef-TK_Saber.h
@@ -1,7 +1,9 @@
-#ifndef HWDEF_TK_SABER_H
-#define HWDEF_TK_SABER_H
+// TK 4-color lightsaber driver layout
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
-/* TK 4-color lightsaber driver layout
+/*
* ----
* Reset -|1 8|- VCC
* PWM 4 (A) -|2 7|- e-switch
@@ -33,4 +35,3 @@
#define LAYOUT_DEFINED
-#endif
diff --git a/hwdef-blf-gt-mini.h b/hwdef-blf-gt-mini.h
new file mode 100644
index 0000000..f4a4d7b
--- /dev/null
+++ b/hwdef-blf-gt-mini.h
@@ -0,0 +1,14 @@
+// BLF/Lumintop GT Mini driver layout
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+// exactly the same as a D1S, but with a lighted button
+// (which is the same hwdef as a D4)
+#include "hwdef-emisar-d4.h"
+
+// lighted button
+#ifndef AUXLED_PIN
+#define AUXLED_PIN PB4 // pin 3
+#endif
+
diff --git a/hwdef-blf-gt.h b/hwdef-blf-gt.h
new file mode 100644
index 0000000..fdb0cb6
--- /dev/null
+++ b/hwdef-blf-gt.h
@@ -0,0 +1,102 @@
+// BLF GT driver layout
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * ----
+ * Reset -|1 8|- VCC (unused)
+ * eswitch -|2 7|- Voltage divider
+ * AUX LED -|3 6|- Current control (buck level)
+ * GND -|4 5|- PWM (buck output on/off)
+ * ----
+ *
+ * On high modes, the buck regulator's current level is adjusted by pin 6.
+ * On low modes, the buck is set to ~10% power
+ * and its output gets PWM'd by pin 5.
+ */
+
+#define ATTINY 85
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-emisar-d4.c
+
+// channel modes
+// * 0. main LEDs
+#define NUM_CHANNEL_MODES 1
+enum CHANNEL_MODES {
+ CM_MAIN = 0,
+};
+
+#define DEFAULT_CHANNEL_MODE CM_MAIN
+
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b00000001
+
+
+#define PWM_CHANNELS 2 // old, remove this
+
+#define PWM_BITS 8 // attiny85 only supports up to 8 bits
+#define PWM_GET PWM_GET8
+#define PWM_DATATYPE uint8_t
+#define PWM_DATATYPE2 uint16_t
+#define PWM1_DATATYPE uint8_t // low modes (PWM with buck at 10% power)
+#define PWM2_DATATYPE uint8_t // high modes (adjustable constant current)
+
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+
+// low modes (PWM turns regulator on/off)
+#define CH1_PIN PB0 // pin 5
+#define CH1_PWM OCR0A // OCR0A is the output compare register for PB0
+
+// high modes (control voltage sets regulator's level)
+#define CH2_PIN PB1 // pin 6
+#define CH2_PWM OCR0B // OCR0B is the output compare register for PB1
+
+#define AUXLED_PIN PB4 // pin 3
+
+// e-switch
+#define SWITCH_PIN PB3 // pin 2
+#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt
+
+// VCC is regulated, so measure battery on pin 7 instead
+#define USE_VOLTAGE_DIVIDER // use a voltage divider on pin 7, not VCC
+#define VOLTAGE_PIN PB2 // pin 7, voltage ADC
+#define VOLTAGE_CHANNEL 0x01 // MUX 01 corresponds with PB2
+#define VOLTAGE_ADC ADC1D // Digital input disable bit corresponding with PB2
+// inherited from tk-attiny.h
+//#define VOLTAGE_ADC_DIDR DIDR0 // DIDR for ADC1
+// 1.1V reference, left-adjust, ADC1/PB2
+//#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)
+#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)
+#endif
+#ifndef ADC_22
+#define ADC_22 (92*4)
+#endif
+
+#define FAST 0xA3 // fast PWM both channels
+#define PHASE 0xA1 // phase-correct PWM both channels
+
+
+inline void hwdef_setup() {
+ // configure PWM channels
+ DDRB = (1 << CH1_PIN)
+ | (1 << CH2_PIN);
+
+ TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
+ TCCR0A = PHASE;
+
+ // configure e-switch
+ PORTB = (1 << SWITCH_PIN); // e-switch is the only input
+ PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-blf-lt1-t1616.c b/hwdef-blf-lt1-t1616.c
new file mode 100644
index 0000000..9d268a4
--- /dev/null
+++ b/hwdef-blf-lt1-t1616.c
@@ -0,0 +1,210 @@
+// Sofirn LT1-t1616 PWM helpers
+// Copyright (C) 2023 SiteRelEnby, Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#include "chan-aux.c"
+
+
+void set_level_zero();
+
+void set_level_ch1(uint8_t level);
+void set_level_ch2(uint8_t level);
+void set_level_both(uint8_t level);
+void set_level_blend(uint8_t level);
+void set_level_auto(uint8_t level);
+
+bool gradual_tick_ch1(uint8_t gt);
+bool gradual_tick_ch2(uint8_t gt);
+bool gradual_tick_both(uint8_t gt);
+bool gradual_tick_blend(uint8_t gt);
+bool gradual_tick_auto(uint8_t gt);
+
+
+Channel channels[] = {
+ { // channel 1 only
+ .set_level = set_level_ch1,
+ .gradual_tick = gradual_tick_ch1,
+ .has_args = 0
+ },
+ { // channel 2 only
+ .set_level = set_level_ch2,
+ .gradual_tick = gradual_tick_ch2,
+ .has_args = 0
+ },
+ { // both channels, tied together (max "200%" power)
+ .set_level = set_level_both,
+ .gradual_tick = gradual_tick_both,
+ .has_args = 0
+ },
+ { // both channels, manual blend (max "100%" power)
+ .set_level = set_level_blend,
+ .gradual_tick = gradual_tick_blend,
+ .has_args = 1
+ },
+ { // both channels, auto blend
+ .set_level = set_level_auto,
+ .gradual_tick = gradual_tick_auto,
+ .has_args = 1
+ },
+ AUX_CHANNELS
+};
+
+
+void set_level_zero() {
+ // disable timer overflow interrupt
+ // (helps improve button press handling from Off state)
+ DSM_INTCTRL = 0;
+
+ // turn off all LEDs
+ ch1_dsm_lvl = 0;
+ ch2_dsm_lvl = 0;
+ CH1_PWM = 0;
+ CH2_PWM = 0;
+ PWM_CNT = 0;
+}
+
+void set_hw_levels(PWM_DATATYPE ch1, PWM_DATATYPE ch2) {
+
+ bool was_on = (CH1_PWM>0) || (CH2_PWM>0);
+
+ // set delta-sigma soft levels
+ ch1_dsm_lvl = ch1;
+ ch2_dsm_lvl = ch2;
+
+ // set hardware PWM levels and init dsm loop
+ CH1_PWM = ch1_pwm = ch1 >> 7;
+ CH2_PWM = ch2_pwm = ch2 >> 7;
+
+ // enable timer overflow interrupt so DSM can work
+ DSM_INTCTRL = DSM_OVF_bm;
+
+ // reset phase when turning on
+ if (! was_on) PWM_CNT = 0;
+
+}
+
+// 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;
+ CH2_PWM = ch2_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;
+
+ // repeat for other channels
+
+ ch2_dsm += (ch2_dsm_lvl & 0x007f);
+ ch2_pwm = (ch2_dsm_lvl >> 7) + (ch2_dsm > 0x7f);
+ ch2_dsm &= 0x7f;
+
+ // clear the interrupt flag to indicate it was handled
+ // as per: https://onlinedocs.microchip.com/pr/GUID-C37FFBA8-82C6-4339-A2B1-ABD9A0F6C162-en-US-8/index.html?GUID-C2A2BEFD-158F-413D-B9D4-0F0556AA79BD
+ DSM_INTFLAGS = DSM_OVF_bm;
+}
+
+
+void set_level_ch1(uint8_t level) {
+ set_hw_levels(PWM_GET(pwm1_levels, level), 0);
+}
+
+void set_level_ch2(uint8_t level) {
+ set_hw_levels(0, PWM_GET(pwm1_levels, level));
+}
+
+void set_level_both(uint8_t level) {
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, level);
+ set_hw_levels(pwm, pwm);
+}
+
+void blend_helper(PWM_DATATYPE *warm, PWM_DATATYPE *cool, uint8_t level) {
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
+ uint8_t blend;
+ if (channel_mode == CM_AUTO) {
+ blend = 255 * (uint16_t)level / RAMP_SIZE;
+ if (cfg.channel_mode_args[channel_mode] & 0b01000000)
+ blend = 255 - blend;
+ } else {
+ blend = cfg.channel_mode_args[channel_mode];
+ }
+
+ calc_2ch_blend(warm, cool, brightness, DSM_TOP, blend);
+}
+
+void set_level_blend(uint8_t level) {
+ PWM_DATATYPE warm, cool;
+ blend_helper(&warm, &cool, level);
+ set_hw_levels(warm, cool);
+}
+
+void set_level_auto(uint8_t level) {
+ PWM_DATATYPE warm, cool;
+ blend_helper(&warm, &cool, level);
+ set_hw_levels(warm, cool);
+}
+
+///// "gradual tick" functions for smooth thermal regulation /////
+// (and other smooth adjustments)
+
+///// bump each channel toward a target value /////
+bool gradual_adjust(PWM_DATATYPE ch1, PWM_DATATYPE ch2) {
+ // 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);
+
+ steps = ch2_dsm_lvl >> shift;
+ for (uint8_t i=0; i<=steps; i++)
+ GRADUAL_ADJUST_SIMPLE(ch2, ch2_dsm_lvl);
+
+ if ((ch1 == ch1_dsm_lvl)
+ && (ch2 == ch2_dsm_lvl )) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
+bool gradual_tick_ch1(uint8_t gt) {
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(pwm, 0);
+}
+
+bool gradual_tick_ch2(uint8_t gt) {
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(0, pwm);
+}
+
+bool gradual_tick_both(uint8_t gt) {
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(pwm, pwm);
+}
+
+bool gradual_tick_blend(uint8_t gt) {
+ PWM_DATATYPE warm, cool;
+ blend_helper(&warm, &cool, gt);
+ return gradual_adjust(warm, cool);
+}
+
+bool gradual_tick_auto(uint8_t gt) {
+ PWM_DATATYPE warm, cool;
+ blend_helper(&warm, &cool, gt);
+ return gradual_adjust(warm, cool);
+}
+
+
diff --git a/hwdef-blf-lt1-t1616.h b/hwdef-blf-lt1-t1616.h
new file mode 100644
index 0000000..7c1f10b
--- /dev/null
+++ b/hwdef-blf-lt1-t1616.h
@@ -0,0 +1,161 @@
+// BLF LT1 driver layout using the Attiny1616
+// Copyright (C) 2021-2023 (gchart), Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * Driver pinout:
+ * eSwitch: PA5
+ * Aux LED: PB5
+ * PWM cool: PB0 (TCA0 WO0)
+ * PWM warm: PB1 (TCA0 WO1)
+ * Voltage: VCC
+ */
+
+#define ATTINY 1616
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-blf-lt1-t1616.c
+
+// allow using aux LEDs as extra channel modes
+#include "chan-aux.h"
+
+// channel modes:
+// * 0. warm only
+// * 1. cool only
+// * 2. both channels, tied together, max "200%" power
+// * 3. both channels, manual blend, max "100%" power
+// * 4. both channels, auto blend, reversible
+#define NUM_CHANNEL_MODES 6
+enum channel_modes_e {
+ CM_CH1 = 0,
+ CM_CH2,
+ CM_BOTH,
+ CM_BLEND,
+ CM_AUTO,
+ CM_AUX
+};
+
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b00011000
+#define USE_CHANNEL_MODE_ARGS
+// _, _, _, 128=middle CCT, 0=warm-to-cool
+#define CHANNEL_MODE_ARGS 0,0,0,128,0,0
+
+// can use some of the common handlers
+#define USE_CALC_2CH_BLEND
+
+
+#define PWM_CHANNELS 1 // old, remove this
+
+#define PWM_BITS 16 // 0 to 32640 (0 to 255 PWM + 0 to 127 DSM) at constant kHz
+#define PWM_GET PWM_GET16
+#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 // 15-bit PWM+DSM ramp
+
+// PWM parameters of both channels are tied together because they share a counter
+// dynamic PWM
+#define PWM_TOP TCA0.SINGLE.PERBUF // holds the TOP value for for variable-resolution PWM
+#define PWM_TOP_INIT 255
+#define PWM_CNT TCA0.SINGLE.CNT // for resetting phase after each TOP adjustment
+// (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 TCA0_OVF_vect
+#define DSM_INTCTRL TCA0.SINGLE.INTCTRL
+#define DSM_INTFLAGS TCA0.SINGLE.INTFLAGS
+#define DSM_OVF_bm TCA_SINGLE_OVF_bm
+
+#define DELAY_FACTOR 90 // less time in delay() because more time spent in interrupts
+
+// warm LEDs
+uint16_t ch1_dsm_lvl;
+uint8_t ch1_pwm, ch1_dsm;
+#define CH1_PIN PB1
+#define CH1_PWM TCA0.SINGLE.CMP1BUF // CMP1 is the output compare register for PB1
+
+// cold LEDs
+uint16_t ch2_dsm_lvl;
+uint8_t ch2_pwm, ch2_dsm;
+#define CH2_PIN PB0
+#define CH2_PWM TCA0.SINGLE.CMP0BUF // CMP0 is the output compare register for PB0
+
+// lighted button
+#define AUXLED_PIN PIN5_bp
+#define AUXLED_PORT PORTB
+
+// e-switch
+#define SWITCH_PIN PIN5_bp
+#define SWITCH_PORT VPORTA.IN
+#define SWITCH_ISC_REG PORTA.PIN2CTRL
+#define SWITCH_VECT PORTA_PORT_vect
+#define SWITCH_INTFLG VPORTA.INTFLAGS
+
+// average drop across diode on this hardware
+#ifndef VOLTAGE_FUDGE_FACTOR
+#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V
+#endif
+
+
+inline void hwdef_setup() {
+
+ // set up the system clock to run at 10 MHz instead of the default 3.33 MHz
+ _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB,
+ CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm );
+
+ //VPORTA.DIR = ...;
+ // Outputs
+ VPORTB.DIR = PIN0_bm // cool white
+ | PIN1_bm // warm white
+ // | PIN2_bm // for testing on LT1S Pro, disable red channel
+ | PIN5_bm; // aux LED
+ //VPORTC.DIR = ...;
+
+ // enable pullups on the unused pins to reduce power
+ PORTA.PIN0CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN1CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN2CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN3CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN4CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN5CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // eSwitch
+ PORTA.PIN6CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN7CTRL = PORT_PULLUPEN_bm;
+
+ //PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // cold tint channel
+ //PORTB.PIN1CTRL = PORT_PULLUPEN_bm; // warm tint channel
+ PORTB.PIN2CTRL = PORT_PULLUPEN_bm; // comment out for testing on LT1S Pro
+ PORTB.PIN3CTRL = PORT_PULLUPEN_bm;
+ PORTB.PIN4CTRL = PORT_PULLUPEN_bm;
+ //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // Aux LED
+
+ PORTC.PIN0CTRL = PORT_PULLUPEN_bm;
+ PORTC.PIN1CTRL = PORT_PULLUPEN_bm;
+ PORTC.PIN2CTRL = PORT_PULLUPEN_bm;
+ PORTC.PIN3CTRL = PORT_PULLUPEN_bm;
+
+ // set up the PWM
+ // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf
+ // PB0 is TCA0:WO0, use TCA_SINGLE_CMP0EN_bm
+ // PB1 is TCA0:WO1, use TCA_SINGLE_CMP1EN_bm
+ // For Fast (Single Slope) PWM use TCA_SINGLE_WGMODE_SINGLESLOPE_gc
+ // For Phase Correct (Dual Slope) PWM use TCA_SINGLE_WGMODE_DSBOTTOM_gc
+ // TODO: add references to MCU documentation
+ TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm
+ | TCA_SINGLE_CMP1EN_bm
+ | TCA_SINGLE_WGMODE_DSBOTTOM_gc;
+ TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc
+ | TCA_SINGLE_ENABLE_bm;
+
+ 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
+
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-blf-lt1.c b/hwdef-blf-lt1.c
new file mode 100644
index 0000000..8a4c0e1
--- /dev/null
+++ b/hwdef-blf-lt1.c
@@ -0,0 +1,204 @@
+// BLF LT1 PWM functions
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+
+void set_level_zero();
+
+void set_level_ch1(uint8_t level);
+void set_level_ch2(uint8_t level);
+void set_level_both(uint8_t level);
+void set_level_blend(uint8_t level);
+//void set_level_auto(uint8_t level); // redundant
+
+#if 0 // gradual adjustments are disabled to save space
+bool gradual_tick_ch1(uint8_t gt);
+bool gradual_tick_ch2(uint8_t gt);
+bool gradual_tick_both(uint8_t gt);
+bool gradual_tick_blend(uint8_t gt);
+//bool gradual_tick_auto(uint8_t gt); // redundant
+#endif
+
+
+Channel channels[] = {
+ { // channel 1 only
+ .set_level = set_level_ch1,
+ //.gradual_tick = gradual_tick_ch1,
+ .has_args = 0
+ },
+ { // channel 2 only
+ .set_level = set_level_ch2,
+ //.gradual_tick = gradual_tick_ch2,
+ .has_args = 0
+ },
+ { // both channels, tied together (max "200%" power)
+ .set_level = set_level_both,
+ //.gradual_tick = gradual_tick_both,
+ .has_args = 0
+ },
+ { // both channels, manual blend (max "100%" power)
+ .set_level = set_level_blend,
+ //.gradual_tick = gradual_tick_blend,
+ .has_args = 1
+ },
+ { // both channels, auto blend
+ .set_level = set_level_blend,
+ //.gradual_tick = gradual_tick_blend,
+ .has_args = 1
+ },
+};
+
+void set_level_zero() {
+ // disable timer 0 overflow interrupt
+ // (helps improve button press handling from Off state)
+ DSM_INTCTRL &= ~DSM_OVF_bm;
+
+ // turn off all LEDs
+ ch1_dsm_lvl = 0;
+ ch2_dsm_lvl = 0;
+ CH1_PWM = 0;
+ CH2_PWM = 0;
+}
+
+// wrap setting the dsm vars, to get a faster response
+// (just setting *_dsm_lvl doesn't work well for strobes)
+void set_hw_levels(PWM_DATATYPE ch1, PWM_DATATYPE ch2) {
+ // set delta-sigma soft levels
+ ch1_dsm_lvl = ch1;
+ ch2_dsm_lvl = ch2;
+
+ // set hardware PWM levels and init dsm loop
+ CH1_PWM = ch1_pwm = ch1 >> 7;
+ CH2_PWM = ch2_pwm = ch2 >> 7;
+
+ // enable timer overflow interrupt so DSM can work
+ DSM_INTCTRL |= DSM_OVF_bm;
+}
+
+// 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;
+ CH2_PWM = ch2_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;
+
+ // repeat for other channels
+
+ ch2_dsm += (ch2_dsm_lvl & 0x007f);
+ ch2_pwm = (ch2_dsm_lvl >> 7) + (ch2_dsm > 0x7f);
+ ch2_dsm &= 0x7f;
+}
+
+
+void set_level_ch1(uint8_t level) {
+ set_hw_levels(PWM_GET(pwm1_levels, level), 0);
+}
+
+void set_level_ch2(uint8_t level) {
+ set_hw_levels(0, PWM_GET(pwm1_levels, level));
+}
+
+void set_level_both(uint8_t level) {
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, level);
+ set_hw_levels(pwm, pwm);
+}
+
+void blend_helper(PWM_DATATYPE *warm, PWM_DATATYPE *cool, uint8_t level) {
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
+ uint8_t blend;
+ if (channel_mode == CM_AUTO) {
+ blend = 255 * (uint16_t)level / RAMP_SIZE;
+ if (cfg.channel_mode_args[channel_mode] & 0b01000000)
+ blend = 255 - blend;
+ } else {
+ blend = cfg.channel_mode_args[channel_mode];
+ }
+
+ calc_2ch_blend(warm, cool, brightness, DSM_TOP, blend);
+}
+
+void set_level_blend(uint8_t level) {
+ PWM_DATATYPE warm, cool;
+ blend_helper(&warm, &cool, level);
+ set_hw_levels(warm, cool);
+}
+
+/*
+void set_level_auto(uint8_t level) {
+ PWM_DATATYPE warm, cool;
+ blend_helper(&warm, &cool, level);
+ set_hw_levels(warm, cool);
+}
+*/
+
+///// "gradual tick" functions for smooth thermal regulation /////
+// (and other smooth adjustments)
+
+#if 0 // disabled to save space
+///// bump each channel toward a target value /////
+bool gradual_adjust(PWM_DATATYPE ch1, PWM_DATATYPE ch2) {
+ // 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);
+
+ steps = ch2_dsm_lvl >> shift;
+ for (uint8_t i=0; i<=steps; i++)
+ GRADUAL_ADJUST_SIMPLE(ch2, ch2_dsm_lvl);
+
+ if ((ch1 == ch1_dsm_lvl)
+ && (ch2 == ch2_dsm_lvl )) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
+bool gradual_tick_ch1(uint8_t gt) {
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(pwm, 0);
+}
+
+bool gradual_tick_ch2(uint8_t gt) {
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(0, pwm);
+}
+
+bool gradual_tick_both(uint8_t gt) {
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(pwm, pwm);
+}
+
+bool gradual_tick_blend(uint8_t gt) {
+ PWM_DATATYPE warm, cool;
+ blend_helper(&warm, &cool, gt);
+ return gradual_adjust(warm, cool);
+}
+
+/*
+bool gradual_tick_auto(uint8_t gt) {
+ PWM_DATATYPE warm, cool;
+ blend_helper(&warm, &cool, gt);
+ return gradual_adjust(warm, cool);
+}
+*/
+
+#endif // if 0
+
diff --git a/hwdef-blf-lt1.h b/hwdef-blf-lt1.h
new file mode 100644
index 0000000..b113fd4
--- /dev/null
+++ b/hwdef-blf-lt1.h
@@ -0,0 +1,114 @@
+// BLF LT1 driver layout
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * ----
+ * Reset -|1 8|- VCC
+ * eswitch -|2 7|- (unused)
+ * aux LED -|3 6|- PWM (5000K)
+ * GND -|4 5|- PWM (3000K)
+ * ----
+ */
+
+#define ATTINY 85
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-blf-lt1.c
+
+// channel modes:
+// * 0. channel 1 only
+// * 1. channel 2 only
+// * 2. both channels, tied together, max "200%" power
+// * 3. both channels, manual blend, max "100%" power
+// * 4. both channels, auto blend, reversible
+#define NUM_CHANNEL_MODES 5
+enum channel_modes_e {
+ CM_CH1 = 0,
+ CM_CH2,
+ CM_BOTH,
+ CM_BLEND,
+ CM_AUTO,
+};
+
+
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b00011000
+#define USE_CHANNEL_MODE_ARGS
+// _, _, _, 128=middle CCT, 0=warm-to-cool
+#define CHANNEL_MODE_ARGS 0,0,0,128,0
+
+// can use some of the common handlers
+#define USE_CALC_2CH_BLEND
+
+
+#define PWM_CHANNELS 1 // old, remove this
+
+#define PWM_BITS 16 // 8-bit hardware PWM + 16-bit DSM
+
+#define PWM_GET PWM_GET16
+#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 // 15-bit PWM+DSM ramp
+
+// PWM parameters of both channels are tied together because they share a counter
+#define PWM_TOP_INIT 255
+#define DSM_TOP (255<<7) // 15-bit resolution leaves 1 bit for carry
+
+// timer interrupt for DSM
+#define DSM_vect TIMER0_OVF_vect
+#define DSM_INTCTRL TIMSK
+#define DSM_OVF_bm (1<<TOIE0)
+
+#define DELAY_FACTOR 90 // less time in delay() because more time spent in interrupts
+
+// warm LEDs
+uint16_t ch1_dsm_lvl;
+uint8_t ch1_pwm, ch1_dsm;
+#define CH1_PIN PB1 // pin 6, warm tint PWM
+#define CH1_PWM OCR0B // OCR0B is the output compare register for PB1
+
+// cold LEDs
+uint16_t ch2_dsm_lvl;
+uint8_t ch2_pwm, ch2_dsm;
+#define CH2_PIN PB0 // pin 5, cold tint PWM
+#define CH2_PWM OCR0A // OCR0A is the output compare register for PB0
+
+// lighted button
+#define AUXLED_PIN PB4 // pin 3
+
+// e-switch
+#define SWITCH_PIN PB3 // pin 2
+#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt
+
+#define ADC_PRSCL 0x07 // clk/128
+
+// average drop across diode on this hardware
+#ifndef VOLTAGE_FUDGE_FACTOR
+#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V
+#endif
+
+#define FAST 0xA3 // fast PWM both channels
+#define PHASE 0xA1 // phase-correct PWM both channels
+
+
+inline void hwdef_setup() {
+ // configure PWM channels
+ DDRB = (1 << CH1_PIN)
+ | (1 << CH2_PIN);
+
+ TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
+ TCCR0A = PHASE;
+
+ // enable timer overflow interrupt for DSM purposes
+ //DSM_INTCTRL |= DSM_OVF_bm; // moved to hwdef.c functions instead
+
+ // configure e-switch
+ PORTB = (1 << SWITCH_PIN); // e-switch is the only input
+ PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-blf-q8-t1616.h b/hwdef-blf-q8-t1616.h
new file mode 100644
index 0000000..d9b981f
--- /dev/null
+++ b/hwdef-blf-q8-t1616.h
@@ -0,0 +1,138 @@
+// BLF Q8 driver layout using the Attiny1616
+// Copyright (C) 2021-2023 gchart, Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * (based on Wurkkos TS10 driver layout,
+ * which in turn was based on an older version of this BLF-Q8-t1616 driver)
+ * (should probably merge the two files at some point)
+ * Driver pinout:
+ * eSwitch: PA5
+ * Aux LED: PB5
+ * PWM FET: PB0 (TCA0 WO0)
+ * PWM 1x7135: PB1 (TCA0 WO1)
+ * Voltage: VCC
+ */
+
+#define ATTINY 1616
+#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 hwdef-wurkkos-ts10.c
+
+// allow using aux LEDs as extra channel modes
+#include "chan-aux.h"
+
+// channel modes:
+// * 0. FET+7135 stacked
+// * 1. aux LEDs
+#define NUM_CHANNEL_MODES 2
+enum CHANNEL_MODES {
+ CM_MAIN = 0,
+ CM_AUX
+};
+
+#define DEFAULT_CHANNEL_MODE CM_MAIN
+
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b00000001
+
+
+#define PWM_CHANNELS 2 // old, remove this
+
+#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255
+#define PWM_GET PWM_GET8
+#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255)
+#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255
+#define PWM1_DATATYPE uint8_t // 1x7135 ramp
+#define PWM2_DATATYPE uint8_t // DD FET ramp
+
+// PWM parameters of both channels are tied together because they share a counter
+#define PWM_TOP TCA0.SINGLE.PERBUF // holds the TOP value for for variable-resolution PWM
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+// not necessary when double-buffered "BUF" registers are used
+#define PWM_CNT TCA0.SINGLE.CNT // for resetting phase after each TOP adjustment
+
+// 1x7135 channel
+#define CH1_PIN PB1
+#define CH1_PWM TCA0.SINGLE.CMP1BUF // CMP1 is the output compare register for PB1
+
+// DD FET channel
+#define CH2_PIN PB0
+#define CH2_PWM TCA0.SINGLE.CMP0BUF // CMP0 is the output compare register for PB0
+
+// e-switch
+#define SWITCH_PIN PIN5_bp
+#define SWITCH_PORT VPORTA.IN
+#define SWITCH_ISC_REG PORTA.PIN2CTRL
+#define SWITCH_VECT PORTA_PORT_vect
+#define SWITCH_INTFLG VPORTA.INTFLAGS
+
+// average drop across diode on this hardware
+#ifndef VOLTAGE_FUDGE_FACTOR
+#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V
+#endif
+
+// lighted button
+#define AUXLED_PIN PIN5_bp
+#define AUXLED_PORT PORTB
+
+
+inline void hwdef_setup() {
+
+ // set up the system clock to run at 10 MHz instead of the default 3.33 MHz
+ _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB,
+ CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm );
+
+ //VPORTA.DIR = ...;
+ // Outputs
+ VPORTB.DIR = PIN0_bm // DD FET
+ | PIN1_bm // 7135
+ | PIN5_bm; // Aux LED
+ //VPORTC.DIR = ...;
+
+ // enable pullups on the unused pins to reduce power
+ PORTA.PIN0CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN1CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN2CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN3CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN4CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN5CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // eSwitch
+ PORTA.PIN6CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN7CTRL = PORT_PULLUPEN_bm;
+
+ //PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // FET channel
+ //PORTB.PIN1CTRL = PORT_PULLUPEN_bm; // 7135 channel
+ PORTB.PIN2CTRL = PORT_PULLUPEN_bm;
+ PORTB.PIN3CTRL = PORT_PULLUPEN_bm;
+ PORTB.PIN4CTRL = PORT_PULLUPEN_bm;
+ //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // Aux LED
+
+ PORTC.PIN0CTRL = PORT_PULLUPEN_bm;
+ PORTC.PIN1CTRL = PORT_PULLUPEN_bm;
+ PORTC.PIN2CTRL = PORT_PULLUPEN_bm;
+ PORTC.PIN3CTRL = PORT_PULLUPEN_bm;
+
+ // set up the PWM
+ // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf
+ // PB0 is TCA0:WO0, use TCA_SINGLE_CMP0EN_bm
+ // PB1 is TCA0:WO1, use TCA_SINGLE_CMP1EN_bm
+ // PB2 is TCA0:WO2, use TCA_SINGLE_CMP2EN_bm
+ // For Fast (Single Slope) PWM use TCA_SINGLE_WGMODE_SINGLESLOPE_gc
+ // For Phase Correct (Dual Slope) PWM use TCA_SINGLE_WGMODE_DSBOTTOM_gc
+ // See the manual for other pins, clocks, configs, portmux, etc
+ TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm
+ | TCA_SINGLE_CMP1EN_bm
+ | TCA_SINGLE_WGMODE_DSBOTTOM_gc;
+ TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc
+ | TCA_SINGLE_ENABLE_bm;
+
+ PWM_TOP = PWM_TOP_INIT;
+
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-BLF_Q8.h b/hwdef-blf-q8.h
index 5dac5d2..cdf311d 100644
--- a/hwdef-BLF_Q8.h
+++ b/hwdef-blf-q8.h
@@ -1,9 +1,9 @@
-#ifndef HWDEF_BLF_Q8_H
-#define HWDEF_BLF_Q8_H
+// BLF Q8 driver layout
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
-/* BLF Q8 driver layout
- * Q8 driver is the same as a D4, basically
- */
+// Q8 driver is the same as a D4, basically
// ... except the Q8 has a lighted button
#ifndef AUXLED_PIN
@@ -16,6 +16,5 @@
#endif
// Q8 driver is the same as a D4, basically
-#include "hwdef-Emisar_D4.h"
+#include "hwdef-emisar-d4.h"
-#endif
diff --git a/hwdef-emisar-2ch-fet.c b/hwdef-emisar-2ch-fet.c
new file mode 100644
index 0000000..caf579d
--- /dev/null
+++ b/hwdef-emisar-2ch-fet.c
@@ -0,0 +1,206 @@
+// Emisar generic 2-channel + DD FET w/ tint ramping
+// Copyright (C) 2021-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "chan-rgbaux.c"
+
+
+void set_level_zero();
+
+void set_level_ch1(uint8_t level);
+void set_level_ch2(uint8_t level);
+void set_level_both(uint8_t level);
+void set_level_blend(uint8_t level);
+void set_level_auto(uint8_t level);
+
+bool gradual_tick_ch1(uint8_t gt);
+bool gradual_tick_ch2(uint8_t gt);
+bool gradual_tick_both(uint8_t gt);
+bool gradual_tick_blend(uint8_t gt);
+bool gradual_tick_auto(uint8_t gt);
+
+
+Channel channels[] = {
+ { // channel 1 only
+ .set_level = set_level_ch1,
+ .gradual_tick = gradual_tick_ch1,
+ .has_args = 0
+ },
+ { // channel 2 only
+ .set_level = set_level_ch2,
+ .gradual_tick = gradual_tick_ch2,
+ .has_args = 0
+ },
+ { // both channels, tied together (max "200%" power)
+ .set_level = set_level_both,
+ .gradual_tick = gradual_tick_both,
+ .has_args = 0
+ },
+ { // both channels, manual blend (max "100%" power)
+ .set_level = set_level_blend,
+ .gradual_tick = gradual_tick_blend,
+ .has_args = 1
+ },
+ { // both channels, auto blend
+ .set_level = set_level_auto,
+ .gradual_tick = gradual_tick_auto,
+ .has_args = 1
+ },
+ RGB_AUX_CHANNELS
+};
+
+
+// set new values for both channels,
+// handling any possible combination
+// and any before/after state
+void set_pwms(uint8_t ch1_pwm, uint8_t ch2_pwm, uint8_t ch3_pwm, uint16_t top) {
+ bool was_on = (CH1_PWM>0) | (CH2_PWM>0) | (CH3_PWM>0);
+ bool now_on = (ch1_pwm>0) | (ch2_pwm>0) | (ch3_pwm>0);
+
+ if (! now_on) {
+ CH1_PWM = 0; // linear
+ CH2_PWM = 0; // linear
+ CH3_PWM = 0; // DD FET
+ PWM_TOP = PWM_TOP_INIT;
+ PWM_CNT = 0;
+ CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp
+ CH2_ENABLE_PORT &= ~(1 << CH2_ENABLE_PIN); // disable opamp
+ return;
+ }
+
+ if (ch1_pwm)
+ CH1_ENABLE_PORT |= (1 << CH1_ENABLE_PIN); // enable opamp
+ else
+ CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp
+
+ if (ch2_pwm)
+ CH2_ENABLE_PORT |= (1 << CH2_ENABLE_PIN); // enable opamp
+ else
+ CH2_ENABLE_PORT &= ~(1 << CH2_ENABLE_PIN); // disable opamp
+
+ CH1_PWM = ch1_pwm;
+ CH2_PWM = ch2_pwm;
+ CH3_PWM = ch3_pwm;
+
+ // manual phase sync when changing level while already on
+ if (was_on && now_on) while(PWM_CNT > (top - 32)) {}
+
+ PWM_TOP = top;
+
+ // reset phase when turning on or off
+ //if ((! was_on) | (! now_on)) PWM_CNT = 0;
+ if (! was_on) PWM_CNT = 0;
+}
+
+void set_level_zero() {
+ return set_pwms(0, 0, 0, PWM_TOP_INIT);
+}
+
+void set_level_ch1(uint8_t level) {
+ uint8_t pwm1 = PWM_GET8 (pwm1_levels, level);
+ uint8_t pwm3 = PWM_GET8 (pwm2_levels, level);
+ uint16_t top = PWM_GET16(pwm3_levels, level);
+ set_pwms(pwm1, 0, pwm3, top);
+}
+
+void set_level_ch2(uint8_t level) {
+ uint8_t pwm2 = PWM_GET8 (pwm4_levels, level);
+ uint16_t top = PWM_GET16(pwm5_levels, level);
+ set_pwms(0, pwm2, 0, top);
+}
+
+void set_level_both(uint8_t level) {
+ uint8_t pwm1 = PWM_GET8 (pwm1_levels, level);
+ uint8_t pwm3 = PWM_GET8 (pwm2_levels, level);
+ uint16_t top = PWM_GET16(pwm3_levels, level);
+ set_pwms(pwm1, pwm1, pwm3, top);
+}
+
+void set_level_blend(uint8_t level) {
+ uint16_t pwm1, pwm2;
+ uint8_t pwm3 = PWM_GET8 (pwm2_levels, level); // DD FET
+ //uint16_t brightness = PWM_GET8 (pwm1_levels, level) << 1;
+ uint16_t brightness = PWM_GET8 (pwm1_levels, level) + pwm3;
+ uint16_t top = PWM_GET16(pwm3_levels, level);
+ uint8_t blend = cfg.channel_mode_args[channel_mode];
+
+ calc_2ch_blend(&pwm1, &pwm2, brightness, top, blend);
+
+ set_pwms(pwm1, pwm2, pwm3, top);
+}
+
+void set_level_auto(uint8_t level) {
+ uint16_t pwm1, pwm2;
+ uint8_t brightness = PWM_GET8 (pwm4_levels, level);
+ uint16_t top = PWM_GET16(pwm5_levels, level);
+ uint8_t blend = 255 * (uint16_t)level / RAMP_SIZE;
+ if (cfg.channel_mode_args[channel_mode] & 0b01000000)
+ blend = 255 - blend;
+
+ calc_2ch_blend(&pwm1, &pwm2, brightness, top, blend);
+
+ set_pwms(pwm1, pwm2, 0, top);
+}
+
+
+///// bump each channel toward a target value /////
+bool gradual_adjust(uint8_t ch1_pwm, uint8_t ch2_pwm, uint8_t ch3_pwm) {
+ GRADUAL_ADJUST_STACKED(ch1_pwm, CH1_PWM, PWM_TOP_INIT);
+ GRADUAL_ADJUST_STACKED(ch2_pwm, CH2_PWM, PWM_TOP_INIT);
+ GRADUAL_ADJUST_SIMPLE (ch3_pwm, CH3_PWM);
+
+ // check for completion
+ if ((ch1_pwm == CH1_PWM)
+ && (ch2_pwm == CH2_PWM)
+ && (ch3_pwm == CH3_PWM)) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
+bool gradual_tick_ch1(uint8_t gt) {
+ uint8_t pwm1 = PWM_GET8(pwm1_levels, gt);
+ uint8_t pwm3 = PWM_GET8(pwm2_levels, gt);
+ return gradual_adjust(pwm1, 0, pwm3);
+}
+
+bool gradual_tick_ch2(uint8_t gt) {
+ uint8_t pwm2 = PWM_GET8(pwm4_levels, gt);
+ return gradual_adjust(0, pwm2, 0);
+}
+
+bool gradual_tick_both(uint8_t gt) {
+ uint8_t pwm1 = PWM_GET8(pwm1_levels, gt);
+ uint8_t pwm3 = PWM_GET8(pwm2_levels, gt);
+ return gradual_adjust(pwm1, pwm1, pwm3);
+}
+
+bool gradual_tick_blend(uint8_t level) {
+ uint16_t pwm1, pwm2;
+ uint8_t pwm3 = PWM_GET8 (pwm2_levels, level); // DD FET
+ //uint16_t brightness = PWM_GET8 (pwm1_levels, level) << 1;
+ uint16_t brightness = PWM_GET8 (pwm1_levels, level) + pwm3;
+ uint16_t top = PWM_GET16(pwm3_levels, level);
+ uint8_t blend = cfg.channel_mode_args[channel_mode];
+
+ calc_2ch_blend(&pwm1, &pwm2, brightness, top, blend);
+
+ return gradual_adjust(pwm1, pwm2, pwm3);
+}
+
+bool gradual_tick_auto(uint8_t level) {
+ uint16_t pwm1, pwm2;
+ uint8_t brightness = PWM_GET8 (pwm4_levels, level);
+ uint16_t top = PWM_GET16(pwm5_levels, level);
+ uint8_t blend = 255 * (uint16_t)level / RAMP_SIZE;
+ if (cfg.channel_mode_args[channel_mode] & 0b01000000)
+ blend = 255 - blend;
+
+ calc_2ch_blend(&pwm1, &pwm2, brightness, top, blend);
+
+ return gradual_adjust(pwm1, pwm2, 0);
+}
+
+
diff --git a/hwdef-emisar-2ch-fet.h b/hwdef-emisar-2ch-fet.h
new file mode 100644
index 0000000..d756a0d
--- /dev/null
+++ b/hwdef-emisar-2ch-fet.h
@@ -0,0 +1,209 @@
+// Emisar 2-channel generic w/ tint ramping + DD FET
+// Copyright (C) 2021-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * Pin / Name / Function
+ * 1 PA6 ch2 LED PWM (linear) (PWM1B)
+ * 2 PA5 R: red aux LED (PWM0B)
+ * 3 PA4 G: green aux LED
+ * 4 PA3 B: blue aux LED
+ * 5 PA2 button LED
+ * 6 PA1 Opamp 2 enable (channel 2 LEDs)
+ * 7 PA0 Opamp 1 enable (channel 1 LEDs)
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 ch1 LED PWM (FET) (PWM0A, 8-bit)
+ * 16 PB3 ch1 LED PWM (linear) (PWM1A)
+ * 17 PB2 MISO
+ * 18 PB1 MOSI / battery voltage (ADC6)
+ * 19 PB0 (none)
+ * 20 PA7 e-switch (PCINT7)
+ * ADC12 thermal sensor
+ *
+ * Both sets of LEDs use one pin to turn the Opamp on/off,
+ * and one pin to control the Opamp power level.
+ * The first channel also has a direct-drive FET for turbo.
+ */
+
+#define ATTINY 1634
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-emisar-2ch-fet.c
+
+// allow using aux LEDs as extra channel modes
+#include "chan-rgbaux.h"
+
+// channel modes:
+// * 0. channel 1 only (linear + DD FET)
+// * 1. channel 2 only (linear)
+// * 2. both channels, tied together, max "200%" power + DD FET at top of ramp
+// * 3. both channels, manual blend, max "100%" power + "200%" and DD FET at top of ramp
+// * 4. both channels, auto blend, reversible (linear only)
+#define NUM_CHANNEL_MODES (5 + NUM_RGB_AUX_CHANNEL_MODES)
+enum channel_modes_e {
+ CM_CH1 = 0,
+ CM_CH2,
+ CM_BOTH,
+ CM_BLEND,
+ CM_AUTO,
+ RGB_AUX_ENUMS
+};
+
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b0000000000011111
+#define USE_CHANNEL_MODE_ARGS
+// _, _, _, 128=middle CCT, 0=warm-to-cool
+#define CHANNEL_MODE_ARGS 0,0,0,128,0,RGB_AUX_CM_ARGS
+
+// can use some of the common handlers
+#define USE_CALC_2CH_BLEND
+
+
+#define PWM_CHANNELS 3 // old, remove this
+
+#define PWM_BITS 16 // 0 to 16383 at variable Hz, not 0 to 255 at 16 kHz
+#define PWM_GET PWM_GET8
+#define PWM_DATATYPE uint16_t
+#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255
+#define PWM1_DATATYPE uint8_t // linear part of linear+FET ramp
+#define PWM2_DATATYPE uint8_t // DD FET part of linear+FET ramp
+#define PWM3_DATATYPE uint16_t // linear+FET ramp tops
+#define PWM4_DATATYPE uint8_t // linear-only ramp
+#define PWM5_DATATYPE uint16_t // linear-only ramp tops
+
+// PWM parameters of both channels are tied together because they share a counter
+#define PWM_TOP ICR1 // holds the TOP value for for variable-resolution PWM
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+#define PWM_CNT TCNT1 // for dynamic PWM, reset phase
+
+// main LEDs, linear
+#define CH1_PIN PB3 // pin 16, Opamp reference
+#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3
+#define CH1_ENABLE_PIN PA0 // pin 7, Opamp power
+#define CH1_ENABLE_PORT PORTA // control port for PA0
+
+// 2nd LEDs, linear
+#define CH2_PIN PA6 // pin 1, 2nd LED Opamp reference
+#define CH2_PWM OCR1B // OCR1B is the output compare register for PA6
+#define CH2_ENABLE_PIN PA1 // pin 6, Opamp power
+#define CH2_ENABLE_PORT PORTA // control port for PA1
+
+// main LEDs, DD FET
+#define CH3_PIN PC0 // pin 15, DD FET PWM
+#define CH3_PWM OCR0A // OCR0A is the output compare register for PC0
+
+// e-switch
+#ifndef SWITCH_PIN
+#define SWITCH_PIN PA7 // pin 20
+#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt
+#define SWITCH_PCIE PCIE0 // PCIE1 is for PCINT[7:0]
+#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]
+#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
+
+// this light has 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
+#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
+
+#define BUTTON_LED_PIN PA2 // pin 5
+#define BUTTON_LED_PORT PORTA // for all "PA" pins
+#define BUTTON_LED_DDR DDRA // for all "PA" pins
+#define BUTTON_LED_PUE PUEA // for all "PA" pins
+
+
+inline void hwdef_setup() {
+ // enable output ports
+ DDRC = (1 << CH3_PIN);
+ DDRB = (1 << CH1_PIN);
+ DDRA = (1 << CH2_PIN)
+ | (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ | (1 << BUTTON_LED_PIN)
+ | (1 << CH1_ENABLE_PIN)
+ | (1 << CH2_ENABLE_PIN)
+ ;
+
+ // 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
+ // Linear opamp PWM for both main and 2nd LEDs (10-bit)
+ // WGM1[3:0]: 1,0,1,0: PWM, Phase Correct, 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) | (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) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5)
+ ;
+
+ // FET PWM (8-bit; this channel can't do 10-bit)
+ // WGM0[2:0]: 0,0,1: PWM, Phase Correct, 8-bit (DS table 11-8)
+ // CS0[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 11-9)
+ // COM0A[1:0]: 1,0: PWM OC0A in the normal direction (DS table 11-4)
+ // COM0B[1:0]: 1,0: PWM OC0B in the normal direction (DS table 11-7)
+ TCCR0A = (0<<WGM01) | (1<<WGM00) // 8-bit (TOP=0xFF) (DS table 11-8)
+ | (1<<COM0A1) | (0<<COM0A0) // PWM 0A in normal direction (DS table 11-4)
+ //| (1<<COM0B1) | (0<<COM0B0) // PWM 0B in normal direction (DS table 11-7)
+ ;
+ TCCR0B = (0<<CS02) | (0<<CS01) | (1<<CS00) // clk/1 (no prescaling) (DS table 11-9)
+ | (0<<WGM02) // phase-correct PWM (DS table 11-8)
+ ;
+
+ // set PWM resolution
+ PWM_TOP = PWM_TOP_INIT;
+
+ // set up e-switch
+ SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch
+ SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-emisar-2ch.c b/hwdef-emisar-2ch.c
new file mode 100644
index 0000000..b09b681
--- /dev/null
+++ b/hwdef-emisar-2ch.c
@@ -0,0 +1,193 @@
+// Emisar 2-channel generic w/ tint ramping
+// Copyright (C) 2021-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#include "chan-rgbaux.c"
+
+
+void set_level_zero();
+
+void set_level_ch1(uint8_t level);
+void set_level_ch2(uint8_t level);
+void set_level_both(uint8_t level);
+void set_level_blend(uint8_t level);
+void set_level_auto(uint8_t level);
+
+bool gradual_tick_ch1(uint8_t gt);
+bool gradual_tick_ch2(uint8_t gt);
+bool gradual_tick_both(uint8_t gt);
+bool gradual_tick_blend(uint8_t gt);
+bool gradual_tick_auto(uint8_t gt);
+
+
+Channel channels[] = {
+ { // channel 1 only
+ .set_level = set_level_ch1,
+ .gradual_tick = gradual_tick_ch1,
+ .has_args = 0
+ },
+ { // channel 2 only
+ .set_level = set_level_ch2,
+ .gradual_tick = gradual_tick_ch2,
+ .has_args = 0
+ },
+ { // both channels, tied together (max "200%" power)
+ .set_level = set_level_both,
+ .gradual_tick = gradual_tick_both,
+ .has_args = 0
+ },
+ { // both channels, manual blend (max "100%" power)
+ .set_level = set_level_blend,
+ .gradual_tick = gradual_tick_blend,
+ .has_args = 1
+ },
+ { // both channels, auto blend
+ .set_level = set_level_auto,
+ .gradual_tick = gradual_tick_auto,
+ .has_args = 1
+ },
+ RGB_AUX_CHANNELS
+};
+
+
+// set new values for both channels,
+// handling any possible combination
+// and any before/after state
+void set_pwms(uint16_t ch1_pwm, uint16_t ch2_pwm, uint16_t top) {
+ bool was_on = (CH1_PWM>0) | (CH2_PWM>0);
+ bool now_on = (ch1_pwm>0) | (ch2_pwm>0);
+
+ if (! now_on) {
+ CH1_PWM = 0;
+ CH2_PWM = 0;
+ PWM_TOP = PWM_TOP_INIT;
+ PWM_CNT = 0;
+ CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp
+ CH2_ENABLE_PORT &= ~(1 << CH2_ENABLE_PIN); // disable opamp
+ return;
+ }
+
+ if (ch1_pwm)
+ CH1_ENABLE_PORT |= (1 << CH1_ENABLE_PIN); // enable opamp
+ else
+ CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp
+
+ if (ch2_pwm)
+ CH2_ENABLE_PORT |= (1 << CH2_ENABLE_PIN); // enable opamp
+ else
+ CH2_ENABLE_PORT &= ~(1 << CH2_ENABLE_PIN); // disable opamp
+
+ CH1_PWM = ch1_pwm;
+ CH2_PWM = ch2_pwm;
+
+ // manual phase sync when changing level while already on
+ if (was_on && now_on) while(PWM_CNT > (top - 32)) {}
+
+ PWM_TOP = top;
+
+ // reset phase when turning on or off
+ //if ((! was_on) | (! now_on)) PWM_CNT = 0;
+ if (! was_on) PWM_CNT = 0;
+}
+
+void set_level_zero() {
+ return set_pwms(0, 0, PWM_TOP_INIT);
+}
+
+void set_level_ch1(uint8_t level) {
+ uint16_t pwm = PWM_GET(pwm1_levels, level);
+ uint16_t top = PWM_GET(pwm_tops, level);
+ set_pwms(pwm, 0, top);
+}
+
+void set_level_ch2(uint8_t level) {
+ uint16_t pwm = PWM_GET(pwm1_levels, level);
+ uint16_t top = PWM_GET(pwm_tops, level);
+ set_pwms(0, pwm, top);
+}
+
+void set_level_both(uint8_t level) {
+ uint16_t pwm = PWM_GET(pwm1_levels, level);
+ uint16_t top = PWM_GET(pwm_tops, level);
+ set_pwms(pwm, pwm, top);
+}
+
+void set_level_blend(uint8_t level) {
+ PWM_DATATYPE ch1_pwm, ch2_pwm;
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
+ PWM_DATATYPE top = PWM_GET(pwm_tops, level);
+ uint8_t blend = cfg.channel_mode_args[channel_mode];
+
+ calc_2ch_blend(&ch1_pwm, &ch2_pwm, brightness, top, blend);
+
+ set_pwms(ch1_pwm, ch2_pwm, top);
+}
+
+void set_level_auto(uint8_t level) {
+ PWM_DATATYPE ch1_pwm, ch2_pwm;
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
+ PWM_DATATYPE top = PWM_GET(pwm_tops, level);
+ uint8_t blend = 255 * (uint16_t)level / RAMP_SIZE;
+ if (cfg.channel_mode_args[channel_mode] & 0b01000000)
+ blend = 255 - blend;
+
+ calc_2ch_blend(&ch1_pwm, &ch2_pwm, brightness, top, blend);
+
+ set_pwms(ch1_pwm, ch2_pwm, top);
+}
+
+
+///// bump each channel toward a target value /////
+bool gradual_adjust(uint16_t ch1_pwm, uint16_t ch2_pwm) {
+ GRADUAL_ADJUST_SIMPLE(ch1_pwm, CH1_PWM);
+ GRADUAL_ADJUST_SIMPLE(ch2_pwm, CH2_PWM);
+
+ // check for completion
+ if ((ch1_pwm == CH1_PWM)
+ && (ch2_pwm == CH2_PWM)) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
+bool gradual_tick_ch1(uint8_t gt) {
+ uint16_t pwm = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(pwm, 0);
+}
+
+bool gradual_tick_ch2(uint8_t gt) {
+ uint16_t pwm = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(0, pwm);
+}
+
+bool gradual_tick_both(uint8_t gt) {
+ uint16_t pwm = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(pwm, pwm);
+}
+
+bool gradual_tick_blend(uint8_t gt) {
+ PWM_DATATYPE ch1_pwm, ch2_pwm;
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt);
+ PWM_DATATYPE top = PWM_GET(pwm_tops, gt);
+ uint8_t blend = cfg.channel_mode_args[channel_mode];
+
+ calc_2ch_blend(&ch1_pwm, &ch2_pwm, brightness, top, blend);
+
+ return gradual_adjust(ch1_pwm, ch2_pwm);
+}
+
+bool gradual_tick_auto(uint8_t gt) {
+ PWM_DATATYPE ch1_pwm, ch2_pwm;
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt);
+ PWM_DATATYPE top = PWM_GET(pwm_tops, gt);
+ uint8_t blend = 255 * (uint16_t)gt / RAMP_SIZE;
+ if (cfg.channel_mode_args[channel_mode] & 0b01000000)
+ blend = 255 - blend;
+
+ calc_2ch_blend(&ch1_pwm, &ch2_pwm, brightness, top, blend);
+
+ return gradual_adjust(ch1_pwm, ch2_pwm);
+}
+
+
diff --git a/hwdef-emisar-2ch.h b/hwdef-emisar-2ch.h
new file mode 100644
index 0000000..99e4945
--- /dev/null
+++ b/hwdef-emisar-2ch.h
@@ -0,0 +1,210 @@
+// Emisar 2-channel generic w/ tint ramping
+// Copyright (C) 2021-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * Pin / Name / Function
+ * 1 PA6 ch2 LED PWM (linear) (PWM1B)
+ * 2 PA5 R: red aux LED (PWM0B)
+ * 3 PA4 G: green aux LED
+ * 4 PA3 B: blue aux LED
+ * 5 PA2 button LED
+ * 6 PA1 Opamp 2 enable (channel 2 LEDs)
+ * 7 PA0 Opamp 1 enable (channel 1 LEDs)
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 [unused: ch1 LED PWM (FET) (PWM0A, 8-bit)]
+ * 16 PB3 ch1 LED PWM (linear) (PWM1A)
+ * 17 PB2 MISO
+ * 18 PB1 MOSI / battery voltage (ADC6)
+ * 19 PB0 (none)
+ * 20 PA7 e-switch (PCINT7)
+ * ADC12 thermal sensor
+ *
+ * Both sets of LEDs use one pin to turn the Opamp on/off,
+ * and one pin to control the Opamp power level.
+ */
+
+#define ATTINY 1634
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-emisar-2ch.c
+
+// allow using aux LEDs as extra channel modes
+#include "chan-rgbaux.h"
+
+// channel modes:
+// * 0. channel 1 only
+// * 1. channel 2 only
+// * 2. both channels, tied together, max "200%" power
+// * 3. both channels, manual blend, max "100%" power
+// * 4. both channels, auto blend, reversible
+#define NUM_CHANNEL_MODES (5 + NUM_RGB_AUX_CHANNEL_MODES)
+enum channel_modes_e {
+ CM_CH1 = 0,
+ CM_CH2,
+ CM_BOTH,
+ CM_BLEND,
+ CM_AUTO,
+ RGB_AUX_ENUMS
+};
+
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b0000000000011111
+#define USE_CHANNEL_MODE_ARGS
+// _, _, _, 128=middle CCT, 0=warm-to-cool
+#define CHANNEL_MODE_ARGS 0,0,0,128,0,RGB_AUX_CM_ARGS
+
+// can use some of the common handlers
+#define USE_CALC_2CH_BLEND
+
+
+#define PWM_CHANNELS 2 // old, remove this
+
+#define PWM_BITS 16 // 0 to 16383 at variable Hz, not 0 to 255 at 16 kHz
+#define PWM_GET PWM_GET16
+#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 // regular ramp table
+#define PWM2_DATATYPE uint16_t // max "200% power" ramp table
+//#define PWM3_DATATYPE uint8_t // DD FET ramp table (8-bit only)
+
+// PWM parameters of both channels are tied together because they share a counter
+#define PWM_TOP ICR1 // holds the TOP value for for variable-resolution PWM
+#define PWM_TOP_INIT 511 // highest value used in top half of ramp
+#define PWM_CNT TCNT1 // for dynamic PWM, reset phase
+
+// main LEDs, linear
+#define CH1_PIN PB3 // pin 16, Opamp reference
+#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3
+#define CH1_ENABLE_PIN PA0 // pin 7, Opamp power
+#define CH1_ENABLE_PORT PORTA // control port for PA0
+
+// 2nd LEDs, linear
+#define CH2_PIN PA6 // pin 1, 2nd LED Opamp reference
+#define CH2_PWM OCR1B // OCR1B is the output compare register for PA6
+#define CH2_ENABLE_PIN PA1 // pin 6, Opamp power
+#define CH2_ENABLE_PORT PORTA // control port for PA1
+
+// main LEDs, DD FET
+//#define CH3_PIN PC0 // pin 15, DD FET PWM
+//#define CH3_PWM OCR0A // OCR0A is the output compare register for PC0
+
+// e-switch
+#ifndef SWITCH_PIN
+#define SWITCH_PIN PA7 // pin 20
+#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt
+#define SWITCH_PCIE PCIE0 // PCIE1 is for PCINT[7:0]
+#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]
+#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
+
+// this light has 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
+#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
+
+#define BUTTON_LED_PIN PA2 // pin 5
+#define BUTTON_LED_PORT PORTA // for all "PA" pins
+#define BUTTON_LED_DDR DDRA // for all "PA" pins
+#define BUTTON_LED_PUE PUEA // for all "PA" pins
+
+
+inline void hwdef_setup() {
+ // enable output ports
+ //DDRC = (1 << CH3_PIN);
+ DDRB = (1 << CH1_PIN);
+ DDRA = (1 << CH2_PIN)
+ | (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ | (1 << BUTTON_LED_PIN)
+ | (1 << CH1_ENABLE_PIN)
+ | (1 << CH2_ENABLE_PIN)
+ ;
+
+ // 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
+ // Linear opamp PWM for both main and 2nd LEDs (10-bit)
+ // WGM1[3:0]: 1,0,1,0: PWM, Phase Correct, 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) | (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) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5)
+ ;
+
+ // unused on this driver
+ #if 0
+ // FET PWM (8-bit; this channel can't do 10-bit)
+ // WGM0[2:0]: 0,0,1: PWM, Phase Correct, 8-bit (DS table 11-8)
+ // CS0[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 11-9)
+ // COM0A[1:0]: 1,0: PWM OC0A in the normal direction (DS table 11-4)
+ // COM0B[1:0]: 1,0: PWM OC0B in the normal direction (DS table 11-7)
+ TCCR0A = (0<<WGM01) | (1<<WGM00) // 8-bit (TOP=0xFF) (DS table 11-8)
+ | (1<<COM0A1) | (0<<COM0A0) // PWM 0A in normal direction (DS table 11-4)
+ //| (1<<COM0B1) | (0<<COM0B0) // PWM 0B in normal direction (DS table 11-7)
+ ;
+ TCCR0B = (0<<CS02) | (0<<CS01) | (1<<CS00) // clk/1 (no prescaling) (DS table 11-9)
+ | (0<<WGM02) // phase-correct PWM (DS table 11-8)
+ ;
+ CH3_PWM = 0; // ensure this channel is off, if it exists
+ #endif
+
+ // set PWM resolution
+ PWM_TOP = PWM_TOP_INIT;
+
+ // set up e-switch
+ SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch
+ SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-emisar-d18.h b/hwdef-emisar-d18.h
new file mode 100644
index 0000000..df08221
--- /dev/null
+++ b/hwdef-emisar-d18.h
@@ -0,0 +1,101 @@
+// Emisar D18 (FET+13+1) driver layout
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * ----
+ * Reset -|1 8|- VCC
+ * eswitch -|2 7|- aux LED?
+ * FET -|3 6|- 13x7135
+ * GND -|4 5|- 1x7135
+ * ----
+ */
+
+#define ATTINY 85
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-fw3a.c
+
+// channel modes
+// * 0. FET+N+1 stacked
+#define NUM_CHANNEL_MODES 1
+enum CHANNEL_MODES {
+ CM_MAIN = 0,
+};
+
+#define DEFAULT_CHANNEL_MODE CM_MAIN
+
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b00000001
+
+
+#define PWM_CHANNELS 3 // old, remove this
+
+#define PWM_BITS 8 // attiny85 only supports up to 8 bits
+#define PWM_GET PWM_GET8
+#define PWM_DATATYPE uint8_t
+#define PWM_DATATYPE2 uint16_t
+#define PWM1_DATATYPE uint8_t // 1x7135 ramp
+#define PWM2_DATATYPE uint8_t // 7x7135 ramp
+#define PWM3_DATATYPE uint8_t // DD FET ramp
+
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+
+// 1x7135 channel
+#define CH1_PIN PB0 // pin 5, 1x7135 PWM
+#define CH1_PWM OCR0A // OCR0A is the output compare register for PB0
+
+// 7x7135 channel
+#define CH2_PIN PB1 // pin 6, 7x7135 PWM
+#define CH2_PWM OCR0B // OCR0B is the output compare register for PB1
+
+// DD FET channel
+#define CH3_PIN PB4 // pin 3, FET PWM
+#define CH3_PWM OCR1B // OCR1B is the output compare register for PB4
+
+// e-switch
+#ifndef SWITCH_PIN
+#define SWITCH_PIN PB3 // pin 2
+#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt
+#endif
+
+#ifndef AUXLED_PIN
+#define AUXLED_PIN PB2 // pin 7
+#endif
+#define ADC_PRSCL 0x07 // clk/128
+
+// average drop across diode on this hardware
+#ifndef VOLTAGE_FUDGE_FACTOR
+#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V
+#endif
+
+#define FAST 0xA3 // fast PWM both channels
+#define PHASE 0xA1 // phase-correct PWM both channels
+
+
+inline void hwdef_setup() {
+
+ // configure PWM channels
+ DDRB = (1 << CH1_PIN)
+ | (1 << CH2_PIN)
+ | (1 << CH3_PIN);
+
+ // configure PWM channels
+ TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
+ TCCR0A = PHASE;
+
+ // Second PWM counter is ... weird
+ TCCR1 = _BV (CS10);
+ GTCCR = _BV (COM1B1) | _BV (PWM1B);
+ OCR1C = PWM_TOP_INIT; // Set ceiling value to maximum
+
+ // configure e-switch
+ PORTB = (1 << SWITCH_PIN); // e-switch is the only input
+ PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin
+
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-emisar-d4.c b/hwdef-emisar-d4.c
new file mode 100644
index 0000000..972f682
--- /dev/null
+++ b/hwdef-emisar-d4.c
@@ -0,0 +1,59 @@
+// Emisar D4 PWM helper functions
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+//#ifdef AUXLED_PIN
+#if 0
+#include "chan-aux.c"
+#else
+#define AUX_CHANNELS
+#endif
+
+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
+ },
+ AUX_CHANNELS
+};
+
+
+void set_level_zero() {
+ CH1_PWM = 0;
+ CH2_PWM = 0;
+}
+
+// TODO: implement delta-sigma modulation for better low modes
+
+// single set of LEDs with 2 stacked power channels, DDFET+1 or DDFET+linear
+void set_level_main(uint8_t level) {
+ PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level);
+ PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level);
+
+ CH1_PWM = ch1_pwm;
+ CH2_PWM = ch2_pwm;
+}
+
+bool gradual_tick_main(uint8_t gt) {
+ PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt);
+ PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt);
+
+ GRADUAL_ADJUST_STACKED(pwm1, CH1_PWM, PWM_TOP_INIT);
+ GRADUAL_ADJUST_SIMPLE (pwm2, CH2_PWM);
+
+ if ( (pwm1 == CH1_PWM)
+ && (pwm2 == CH2_PWM)
+ ) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
diff --git a/hwdef-emisar-d4.h b/hwdef-emisar-d4.h
new file mode 100644
index 0000000..7be700a
--- /dev/null
+++ b/hwdef-emisar-d4.h
@@ -0,0 +1,104 @@
+// Emisar D4 driver layout
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * ----
+ * Reset -|1 8|- VCC
+ * eswitch -|2 7|-
+ * AUX LED -|3 6|- PWM (FET)
+ * GND -|4 5|- PWM (1x7135)
+ * ----
+ */
+
+#define ATTINY 85
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-emisar-d4.c
+
+// allow using aux LEDs as extra channel modes (when they exist)
+//#ifdef AUXLED_PIN
+#if 0
+#include "chan-aux.h"
+#else
+#define NUM_AUX_CHANNEL_MODES 0
+#endif
+
+// channel modes
+// * 0. FET+7135 stacked
+// * 1. button LED (only on some derivative models, like BLF Q8)
+#define NUM_CHANNEL_MODES (1 + NUM_AUX_CHANNEL_MODES)
+enum CHANNEL_MODES {
+ CM_MAIN = 0,
+ CM_AUX,
+};
+
+#define DEFAULT_CHANNEL_MODE CM_MAIN
+
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b00000001
+
+
+#define PWM_CHANNELS 2 // old, remove this
+
+#define PWM_BITS 8 // attiny85 only supports up to 8 bits
+#define PWM_GET PWM_GET8
+#define PWM_DATATYPE uint8_t
+#define PWM_DATATYPE2 uint16_t
+#define PWM1_DATATYPE uint8_t // 1x7135 ramp
+#define PWM2_DATATYPE uint8_t // DD FET ramp
+
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+
+// 1x7135 channel
+#ifndef CH1_PIN
+#define CH1_PIN PB0 // pin 5, 1x7135 PWM
+#define CH1_PWM OCR0A // OCR0A is the output compare register for PB0
+#endif
+
+// DD FET channel
+#ifndef CH2_PIN
+#define CH2_PIN PB1 // pin 6, FET PWM
+#define CH2_PWM OCR0B // OCR0B is the output compare register for PB1
+#endif
+
+//#define AUXLED_PIN PB4 // pin 3
+
+// e-switch
+#ifndef SWITCH_PIN
+#define SWITCH_PIN PB3 // pin 2
+#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt
+#endif
+
+// (FIXME: remove? not used?)
+//#define VOLTAGE_PIN PB2 // pin 7, voltage ADC
+//#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
+//#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
+#define ADC_PRSCL 0x07 // clk/128
+
+// average drop across diode on this hardware
+#ifndef VOLTAGE_FUDGE_FACTOR
+#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V
+#endif
+
+#define FAST 0xA3 // fast PWM both channels
+#define PHASE 0xA1 // phase-correct PWM both channels
+
+
+inline void hwdef_setup() {
+ // configure PWM channels
+ DDRB = (1 << CH1_PIN)
+ | (1 << CH2_PIN);
+
+ TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
+ TCCR0A = PHASE;
+
+ // configure e-switch
+ PORTB = (1 << SWITCH_PIN); // e-switch is the only input
+ PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-emisar-d4k-3ch.c b/hwdef-emisar-d4k-3ch.c
new file mode 100644
index 0000000..e35af08
--- /dev/null
+++ b/hwdef-emisar-d4k-3ch.c
@@ -0,0 +1,362 @@
+// Emisar D4K 3-channel hwdef
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#include "spaghetti-monster/anduril/channel-modes.h" //for circular_tint_3h()
+#include "chan-rgbaux.c"
+
+void set_level_zero();
+
+void set_level_main2(uint8_t level);
+void set_level_led3(uint8_t level);
+void set_level_led4(uint8_t level);
+void set_level_all(uint8_t level);
+void set_level_led34a_blend(uint8_t level);
+void set_level_led34b_blend(uint8_t level);
+void set_level_hsv(uint8_t level);
+void set_level_auto3(uint8_t level);
+
+bool gradual_tick_main2(uint8_t gt);
+bool gradual_tick_led3(uint8_t gt);
+bool gradual_tick_led4(uint8_t gt);
+bool gradual_tick_all(uint8_t gt);
+bool gradual_tick_led34a_blend(uint8_t gt);
+bool gradual_tick_led34b_blend(uint8_t gt);
+bool gradual_tick_hsv(uint8_t gt);
+bool gradual_tick_auto3(uint8_t gt);
+
+
+Channel channels[] = {
+ { // main 2 LEDs only
+ .set_level = set_level_main2,
+ .gradual_tick = gradual_tick_main2,
+ .has_args = 0
+ },
+ { // 3rd LED only
+ .set_level = set_level_led3,
+ .gradual_tick = gradual_tick_led3,
+ .has_args = 0
+ },
+ { // 4th LED only
+ .set_level = set_level_led4,
+ .gradual_tick = gradual_tick_led4,
+ .has_args = 0
+ },
+ { // all channels, tied together (equal amounts, max power)
+ .set_level = set_level_all,
+ .gradual_tick = gradual_tick_all,
+ .has_args = 0
+ },
+ { // 3rd + 4th LEDs, manual blend (max "100%" power) (8/16/16)
+ .set_level = set_level_led34a_blend,
+ .gradual_tick = gradual_tick_led34a_blend,
+ .has_args = 1
+ },
+ { // 3rd + 4th LEDs, manual blend (max "100%" power) (16/16/8)
+ .set_level = set_level_led34b_blend,
+ .gradual_tick = gradual_tick_led34b_blend,
+ .has_args = 1
+ },
+ { // 3ch blend (HSV style)
+ .set_level = set_level_hsv,
+ .gradual_tick = gradual_tick_hsv,
+ .has_args = 1
+ },
+ { // 3ch auto blend (red-warm-cool style, led4-led3-main2)
+ .set_level = set_level_auto3,
+ .gradual_tick = gradual_tick_auto3,
+ .has_args = 0
+ },
+ RGB_AUX_CHANNELS
+};
+
+// HSV mode needs a different 3H handler
+StatePtr channel_3H_modes[NUM_CHANNEL_MODES] = {
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, circular_tint_3h, NULL,
+};
+
+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
+ MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN);
+ LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN );
+ LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN );
+ main2_dsm_lvl = 0;
+ led3_dsm_lvl = 0;
+ led4_dsm_lvl = 0;
+ MAIN2_PWM_LVL = 0;
+ LED3_PWM_LVL = 0;
+ LED4_PWM_LVL = 0;
+ PWM_CNT = 0;
+ //PWM_TOP = PWM_TOP_INIT;
+}
+
+// wrap setting the dsm vars, to get a faster response
+// (just setting *_dsm_lvl doesn't work well for strobes)
+void set_hw_levels(PWM_DATATYPE main2, // brightness, 0 to DSM_TOP
+ PWM_DATATYPE led3,
+ PWM_DATATYPE led4,
+ bool main2_en, // enable even at PWM=0?
+ bool led3_en,
+ bool led4_en
+ ) {
+
+ // enable/disable LED power channels
+ if (main2 | main2_en)
+ MAIN2_ENABLE_PORT |= (1 << MAIN2_ENABLE_PIN);
+ else MAIN2_ENABLE_PORT &= ~(1 << MAIN2_ENABLE_PIN);
+
+ if (led3 | led3_en )
+ LED3_ENABLE_PORT |= (1 << LED3_ENABLE_PIN);
+ else LED3_ENABLE_PORT &= ~(1 << LED3_ENABLE_PIN);
+
+ if (led4 | led4_en )
+ LED4_ENABLE_PORT |= (1 << LED4_ENABLE_PIN);
+ else LED4_ENABLE_PORT &= ~(1 << LED4_ENABLE_PIN);
+
+ // set delta-sigma soft levels
+ main2_dsm_lvl = main2;
+ led3_dsm_lvl = led3;
+ led4_dsm_lvl = led4;
+ // set hardware PWM levels and init dsm loop
+ MAIN2_PWM_LVL = main2_pwm = main2 >> 7;
+ LED3_PWM_LVL = led3_pwm = led3 >> 7;
+ LED4_PWM_LVL = led4_pwm = led4 >> 7;
+
+ // enable timer overflow interrupt so DSM can work
+ DSM_INTCTRL |= DSM_OVF_bm;
+
+ // force phase reset
+ PWM_CNT = PWM_CNT2 = 0;
+}
+
+// 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)
+ MAIN2_PWM_LVL = main2_pwm;
+ LED3_PWM_LVL = led3_pwm;
+ LED4_PWM_LVL = led4_pwm;
+
+ // calculate next values, now that timing matters less
+
+ // accumulate error
+ main2_dsm += (main2_dsm_lvl & 0x007f);
+ // next PWM = base PWM value + carry bit
+ main2_pwm = (main2_dsm_lvl >> 7) + (main2_dsm > 0x7f);
+ // clear carry bit
+ main2_dsm &= 0x7f;
+
+ // repeat for other channels
+
+ led3_dsm += (led3_dsm_lvl & 0x007f);
+ led3_pwm = (led3_dsm_lvl >> 7) + (led3_dsm > 0x7f);
+ led3_dsm &= 0x7f;
+
+ led4_dsm += (led4_dsm_lvl & 0x007f);
+ led4_pwm = (led4_dsm_lvl >> 7) + (led4_dsm > 0x7f);
+ led4_dsm &= 0x7f;
+}
+
+// LEDs 1+2 are 8-bit
+// this 8-bit channel may be LEDs 1+2 or LED 4, depending on wiring
+void set_level_main2(uint8_t level) {
+ set_hw_levels(PWM_GET(pwm1_levels, level), 0, 0,
+ 1, 0, 0);
+}
+
+// LED 3 is 16-bit
+void set_level_led3(uint8_t level) {
+ set_hw_levels(0, PWM_GET(pwm1_levels, level), 0,
+ 0, 1, 0);
+}
+
+// this 16-bit channel may be LED 4 or LEDs 1+2, depending on wiring
+void set_level_led4(uint8_t level) {
+ set_hw_levels(0, 0, PWM_GET(pwm1_levels, level),
+ 0, 0, 1);
+}
+
+void set_level_all(uint8_t level) {
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, level);
+ set_hw_levels(pwm, pwm, pwm, 1, 1, 1);
+}
+
+// 8/16/16 wiring, mix 16+16
+void set_level_led34a_blend(uint8_t level) {
+ PWM_DATATYPE warm_PWM, cool_PWM;
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
+ uint8_t blend = cfg.channel_mode_args[channel_mode];
+
+ calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, DSM_TOP, blend);
+
+ set_hw_levels(0, warm_PWM, cool_PWM,
+ 0, (blend<170), (blend>85));
+}
+
+// 16/16/8 wiring, mix 16+8
+void set_level_led34b_blend(uint8_t level) {
+ PWM_DATATYPE warm_PWM, cool_PWM;
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
+ uint8_t blend = cfg.channel_mode_args[channel_mode];
+
+ calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, DSM_TOP, blend);
+
+ set_hw_levels(cool_PWM, warm_PWM, 0,
+ (blend>85), (blend<170), 0);
+}
+
+void set_level_hsv(uint8_t level) {
+ RGB_t color;
+ uint8_t h = cfg.channel_mode_args[channel_mode];
+ uint8_t s = 255; // TODO: drop saturation at brightest levels
+ PWM_DATATYPE v = PWM_GET(pwm1_levels, level);
+ color = hsv2rgb(h, s, v);
+
+ set_hw_levels(color.r, color.g, color.b,
+ 0, 0, 0);
+}
+
+// calculate a 3-channel "auto tint" blend
+// (like red -> warm white -> cool white)
+// results are placed in *a, *b, and *c vars
+// level : ramp level to convert into 3 channel levels
+// (assumes ramp table is "pwm1_levels")
+void calc_auto_3ch_blend(
+ PWM_DATATYPE *a, // red
+ PWM_DATATYPE *b, // warm
+ PWM_DATATYPE *c, // cool
+ uint8_t level) {
+
+ PWM_DATATYPE vpwm = PWM_GET(pwm1_levels, level);
+
+ // tint goes from 0 (red) to 127 (warm white) to 255 (cool white)
+ uint8_t mytint;
+ mytint = 255 * (uint16_t)(level+1) / RAMP_SIZE;
+
+ uint8_t falling=0, rising=0;
+ if (level < (RAMP_SIZE/2))
+ falling = 255 - triangle_wave(mytint);
+ else
+ rising = 255 - triangle_wave(mytint);
+
+ // TODO: make "a" drop to zero sooner, and "c" start ramping up later
+ // red is high at 0, low at 255 (linear)
+ *a = (((PWM_DATATYPE2)falling
+ * (PWM_DATATYPE2)vpwm) + 127) / 255;
+ // warm white is low at 0 and 255, high at 127 (linear triangle)
+ *b = (((PWM_DATATYPE2)triangle_wave(mytint)
+ * (PWM_DATATYPE2)vpwm) ) / 255;
+ // cool white is low at 0, high at 255 (linear)
+ *c = (((PWM_DATATYPE2)rising
+ * (PWM_DATATYPE2)vpwm) + 127) / 255;
+
+}
+
+// 3-channel "auto tint" channel mode
+void set_level_auto3(uint8_t level) {
+ PWM_DATATYPE a, b, c;
+ calc_auto_3ch_blend(&a, &b, &c, level);
+
+ set_hw_levels(c, b, a,
+ 0, 0, (0 == level));
+}
+
+///// "gradual tick" functions for smooth thermal regulation /////
+// (and other smooth adjustments)
+
+bool gradual_adjust(PWM_DATATYPE main2, PWM_DATATYPE led3, PWM_DATATYPE led4) {
+ // 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 = main2_dsm_lvl >> shift;
+ for (uint8_t i=0; i<=steps; i++)
+ GRADUAL_ADJUST_SIMPLE(main2, main2_dsm_lvl);
+
+ steps = led3_dsm_lvl >> shift;
+ for (uint8_t i=0; i<=steps; i++)
+ GRADUAL_ADJUST_SIMPLE(led3, led3_dsm_lvl );
+
+ steps = led4_dsm_lvl >> shift;
+ for (uint8_t i=0; i<=steps; i++)
+ GRADUAL_ADJUST_SIMPLE(led4, led4_dsm_lvl );
+
+ if ((main2 == main2_dsm_lvl)
+ && (led3 == led3_dsm_lvl )
+ && (led4 == led4_dsm_lvl )) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
+bool gradual_tick_main2(uint8_t gt) {
+ PWM_DATATYPE main2 = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(main2, 0, 0);
+}
+
+bool gradual_tick_led3(uint8_t gt) {
+ PWM_DATATYPE led3 = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(0, led3, 0);
+}
+
+bool gradual_tick_led4(uint8_t gt) {
+ PWM_DATATYPE led4 = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(0, 0, led4);
+}
+
+bool gradual_tick_all(uint8_t gt) {
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(pwm, pwm, pwm);
+}
+
+// 8/16/16 wiring, mix 16+16
+bool gradual_tick_led34a_blend(uint8_t gt) {
+ PWM_DATATYPE warm_PWM, cool_PWM;
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt);
+ uint8_t blend = cfg.channel_mode_args[channel_mode];
+
+ calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, DSM_TOP, blend);
+
+ return gradual_adjust(0, warm_PWM, cool_PWM);
+}
+
+// 16/16/8 wiring, mix 16+8
+bool gradual_tick_led34b_blend(uint8_t gt) {
+ PWM_DATATYPE warm_PWM, cool_PWM;
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt);
+ uint8_t blend = cfg.channel_mode_args[channel_mode];
+
+ calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, DSM_TOP, blend);
+
+ return gradual_adjust(cool_PWM, warm_PWM, 0);
+}
+
+bool gradual_tick_hsv(uint8_t gt) {
+ // figure out what exact PWM levels we're aiming for
+ RGB_t color;
+ uint8_t h = cfg.channel_mode_args[channel_mode];
+ uint8_t s = 255; // TODO: drop saturation at brightest levels
+ PWM_DATATYPE v = PWM_GET(pwm1_levels, gt);
+ color = hsv2rgb(h, s, v);
+
+ return gradual_adjust(color.r, color.g, color.b);
+}
+
+bool gradual_tick_auto3(uint8_t gt) {
+ // figure out what exact PWM levels we're aiming for
+ PWM_DATATYPE red, warm, cool;
+ calc_auto_3ch_blend(&red, &warm, &cool, gt);
+ return gradual_adjust(cool, warm, red);
+}
+
diff --git a/hwdef-emisar-d4k-3ch.h b/hwdef-emisar-d4k-3ch.h
new file mode 100644
index 0000000..2e83fbe
--- /dev/null
+++ b/hwdef-emisar-d4k-3ch.h
@@ -0,0 +1,248 @@
+// Emisar D4K 3-channel hwdef
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * Pin / Name / Function
+ * 1 PA6 LED 4 PWM (linear) (PWM1B)
+ * 2 PA5 R: red aux LED (PWM0B)
+ * 3 PA4 G: green aux LED
+ * 4 PA3 B: blue aux LED
+ * 5 PA2 button LED
+ * 6 PA1 3rd LED opamp enable
+ * 7 PA0 4th LED opamp enable
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 main 2 LEDs PWM (linear) (PWM0A) (8-bit only)
+ * 16 PB3 3rd LED PWM (linear) (PWM1A)
+ * 17 PB2 MISO
+ * 18 PB1 MOSI / battery voltage (ADC6)
+ * 19 PB0 main 2 LEDs opamp enable
+ * 20 PA7 e-switch (PCINT7)
+ * ADC12 thermal sensor
+ *
+ * PWM speed + resolution is dynamic on 2 channels,
+ * and 8-bit 16 kHz on 1 channel.
+ *
+ * Note: Some hardware might swap the wires at build time!
+ * It might be wired 8/16/16 (8-bit LEDs 1+2) or 16/16/8 (8-bit LED 4).
+ * So this code should support both wire layouts.
+ */
+
+#define ATTINY 1634
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-emisar-d4k-3ch.c
+
+// allow using aux LEDs as extra channel modes
+#include "chan-rgbaux.h"
+
+// channel modes:
+// - 1. main 2 LEDs only (8/16/16 wiring) or LED 4 only (16/16/8)
+// - 2. 3rd LED only
+// - 3. 4th LED only (8/16/16 wiring) or main 2 LEDs only (16/16/8)
+// - 4. all 3 channels (equal amounts)
+// - 5. 2ch blend (3rd + 4th LEDs, 8/16/16 wiring)
+// - 6. 2ch blend (3rd + 4th LEDs, 16/16/8 wiring)
+// - 7. 3ch blend (HSV style)
+// - 8. 3ch auto blend (red-warm-cool style, led4-led3-main2)
+// - 9+. RGB aux (hidden)
+#define NUM_CHANNEL_MODES (8 + NUM_RGB_AUX_CHANNEL_MODES)
+enum channel_modes_e {
+ CM_MAIN2 = 0,
+ CM_LED3,
+ CM_LED4,
+ CM_ALL,
+ CM_BLEND34A, // 8 / [16+16]
+ CM_BLEND34B, // 16 / [16+8]
+ CM_HSV,
+ CM_AUTO3,
+ RGB_AUX_ENUMS
+};
+
+#define CHANNEL_MODES_ENABLED 0b0000000000001111
+#define USE_CHANNEL_MODE_ARGS
+// _, _, _, _, 128=middle CCT, 128=middle CCT, 213=purple, _
+#define CHANNEL_MODE_ARGS 0,0,0,0,128,128,213,0,RGB_AUX_CM_ARGS
+#define USE_CUSTOM_CHANNEL_3H_MODES
+#define USE_CIRCULAR_TINT_3H
+
+// can use some of the common handlers
+#define USE_CALC_2CH_BLEND
+#define USE_HSV2RGB
+
+
+#define PWM_CHANNELS 1 // old, remove this
+
+#define PWM_BITS 16 // 0 to 16383 at variable Hz, not 0 to 255 at 16 kHz
+#define PWM_GET PWM_GET16
+#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 // 15-bit PWM+DSM ramp
+
+// dynamic PWM
+#define PWM_TOP ICR1 // holds the TOP value for for variable-resolution PWM
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+#define PWM_CNT TCNT1 // for checking / resetting phase
+#define PWM_CNT2 TCNT0 // 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 TIMER0_OVF_vect
+#define DSM_INTCTRL TIMSK
+#define DSM_OVF_bm (1<<TOIE0)
+
+#define DELAY_FACTOR 90 // less time in delay() because more time spent in interrupts
+
+// main 2 LEDs / 1st channel (2 LEDs)
+uint16_t main2_dsm_lvl;
+uint8_t main2_pwm, main2_dsm;
+#define MAIN2_PWM_PIN PC0
+#define MAIN2_PWM_LVL OCR0A // OCR0A is the output compare register for PC0
+#define MAIN2_ENABLE_PIN PB0 // Opamp power
+#define MAIN2_ENABLE_PORT PORTB // control port for PB0
+
+// LED 3 / 2nd channel (1 LED)
+uint16_t led3_dsm_lvl;
+uint8_t led3_pwm, led3_dsm;
+#define LED3_PWM_PIN PB3
+#define LED3_PWM_LVL OCR1A // OCR1A is the output compare register for PB3
+#define LED3_ENABLE_PIN PA1 // Opamp power
+#define LED3_ENABLE_PORT PORTA // control port for PA1
+
+// LED 4 / 3rd channel (1 LED)
+uint16_t led4_dsm_lvl;
+uint8_t led4_pwm, led4_dsm;
+#define LED4_PWM_PIN PA6
+#define LED4_PWM_LVL OCR1B // OCR1B is the output compare register for PA6
+#define LED4_ENABLE_PIN PA0 // Opamp power
+#define LED4_ENABLE_PORT PORTA // control port for PA0
+
+// e-switch
+#ifndef SWITCH_PIN
+#define SWITCH_PIN PA7 // pin 20
+#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt
+#define SWITCH_PCIE PCIE0 // PCIE1 is for PCINT[7:0]
+#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]
+#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
+
+// this light has 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
+#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
+
+#define BUTTON_LED_PIN PA2 // pin 5
+#define BUTTON_LED_PORT PORTA // for all "PA" pins
+#define BUTTON_LED_DDR DDRA // for all "PA" pins
+#define BUTTON_LED_PUE PUEA // for all "PA" pins
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+// the aux LEDs are front-facing, so turn them off while main LEDs are on
+// it also has an independent LED in the button
+#define USE_BUTTON_LED
+
+
+inline void hwdef_setup() {
+ // enable output ports
+ DDRC = (1 << MAIN2_PWM_PIN);
+ DDRB = (1 << LED3_PWM_PIN)
+ | (1 << MAIN2_ENABLE_PIN)
+ ;
+ DDRA = (1 << LED4_PWM_PIN)
+ | (1 << LED4_ENABLE_PIN)
+ | (1 << LED3_ENABLE_PIN)
+ | (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ | (1 << BUTTON_LED_PIN)
+ ;
+
+ // 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
+ // Linear opamp PWM for both 16-bit 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) | (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;
+
+ // 8-bit PWM channel (LEDs 1+2 or LED 4)
+ // WGM0[2:0]: 0,0,1: PWM, Phase Correct, 8-bit (DS table 11-8)
+ // CS0[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 11-9)
+ // COM0A[1:0]: 1,0: PWM OC0A in the normal direction (DS table 11-4)
+ // COM0B[1:0]: 1,0: PWM OC0B in the normal direction (DS table 11-7)
+ TCCR0A = (0<<WGM01) | (1<<WGM00) // 8-bit (TOP=0xFF) (DS table 11-8)
+ | (1<<COM0A1) | (0<<COM0A0) // PWM 0A in normal direction (DS table 11-4)
+ //| (1<<COM0B1) | (0<<COM0B0) // PWM 0B in normal direction (DS table 11-7)
+ ;
+ TCCR0B = (0<<CS02) | (0<<CS01) | (1<<CS00) // clk/1 (no prescaling) (DS table 11-9)
+ | (0<<WGM02) // phase-correct PWM (DS table 11-8)
+ ;
+
+ // 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
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-emisar-d4s.h b/hwdef-emisar-d4s.h
new file mode 100644
index 0000000..95789e6
--- /dev/null
+++ b/hwdef-emisar-d4s.h
@@ -0,0 +1,13 @@
+// Emisar D4S driver layout
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+// same as a D4, basically
+#include "hwdef-emisar-d4.h"
+
+// ... except the D4S has aux LEDs under the optic
+#ifndef AUXLED_PIN
+#define AUXLED_PIN PB4 // pin 3
+#endif
+
diff --git a/hwdef-emisar-d4sv2.c b/hwdef-emisar-d4sv2.c
new file mode 100644
index 0000000..6399fb8
--- /dev/null
+++ b/hwdef-emisar-d4sv2.c
@@ -0,0 +1,67 @@
+// Emisar D4Sv2 PWM helper functions
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "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() {
+ CH1_PWM = 0;
+ CH2_PWM = 0;
+ CH3_PWM = 0;
+ PWM_CNT = 0; // reset phase
+}
+
+// single set of LEDs with 3 stacked power channels, DDFET+3+1
+void set_level_main(uint8_t level) {
+ PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level);
+ PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level);
+ PWM_DATATYPE ch3_pwm = PWM_GET(pwm3_levels, level);
+ // pulse frequency modulation, a.k.a. dynamic PWM
+ uint16_t top = PWM_GET16(pwm_tops, level);
+
+ CH1_PWM = ch1_pwm;
+ CH2_PWM = ch2_pwm;
+ CH3_PWM = ch3_pwm;
+ // wait to sync the counter and avoid flashes
+ while(actual_level && (PWM_CNT > (top - 32))) {}
+ PWM_TOP = top;
+ // force reset phase when turning on from zero
+ // (because otherwise the initial response is inconsistent)
+ if (! actual_level) PWM_CNT = 0;
+}
+
+bool gradual_tick_main(uint8_t gt) {
+ PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt);
+ PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt);
+ PWM_DATATYPE pwm3 = PWM_GET(pwm3_levels, gt);
+
+ GRADUAL_ADJUST_STACKED(pwm1, CH1_PWM, PWM_TOP_INIT);
+ GRADUAL_ADJUST_STACKED(pwm2, CH2_PWM, PWM_TOP_INIT);
+ GRADUAL_ADJUST_SIMPLE (pwm3, CH3_PWM);
+
+ if ( (pwm1 == CH1_PWM)
+ && (pwm2 == CH2_PWM)
+ && (pwm3 == CH3_PWM)
+ ) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
diff --git a/hwdef-emisar-d4sv2.h b/hwdef-emisar-d4sv2.h
new file mode 100644
index 0000000..11d1abb
--- /dev/null
+++ b/hwdef-emisar-d4sv2.h
@@ -0,0 +1,179 @@
+// Emisar D4Sv2 driver layout (attiny1634)
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * (same layout as D4v2, except it's a FET+3+1 instead of FET+1)
+ *
+ * Pin / Name / Function
+ * 1 PA6 FET PWM (PWM1B)
+ * 2 PA5 red aux LED (PWM0B)
+ * 3 PA4 green aux LED
+ * 4 PA3 blue aux LED
+ * 5 PA2 e-switch
+ * 6 PA1 button LED?
+ * 7 PA0 (none)
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 3x7135 PWM (PWM0A) (8-bit)
+ * 16 PB3 1x7135 PWM (PWM1A)
+ * 17 PB2 MISO
+ * 18 PB1 MOSI
+ * 19 PB0 (none)
+ * 20 PA7 (none)
+ * ADC12 thermal sensor
+ */
+
+#define ATTINY 1634
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-emisar-d4sv2.c
+
+// allow using aux LEDs as extra channel modes
+#include "chan-rgbaux.h"
+
+// channel modes:
+// * 0. FET+3+1 stacked
+// * 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
+// no args
+//#define USE_CHANNEL_MODE_ARGS
+//#define CHANNEL_MODE_ARGS 0,0,0,0,0,0,0,0
+
+
+#define PWM_CHANNELS 3 // old, remove this
+
+#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255
+#define PWM_GET PWM_GET8
+#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255)
+#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255
+#define PWM1_DATATYPE uint8_t // 1x7135 ramp (16-bit)
+#define PWM2_DATATYPE uint8_t // 3x7135 ramp (8-bit)
+#define PWM3_DATATYPE uint8_t // DD FET ramp (16-bit)
+
+// PWM parameters of FET and 1x7135 channels are tied together because they share a counter
+#define PWM_TOP ICR1 // holds the TOP value for for variable-resolution PWM
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+#define PWM_CNT TCNT1 // for dynamic PWM, reset phase
+
+// 1x7135 channel
+#define CH1_PIN PB3 // pin 16, 1x7135 PWM
+#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3
+
+// 3x7135 channel
+#define CH2_PIN PC0 // pin 15, 3x7135 PWM
+#define CH2_PWM OCR0A // OCR0A is the output compare register for PC0
+
+// DD FET channel
+#define CH3_PIN PA6 // pin 1, DD FET PWM
+#define CH3_PWM OCR1B // OCR1B is the output compare register for PB1
+
+// e-switch
+#define SWITCH_PIN PA2 // pin 5
+#define SWITCH_PCINT PCINT2 // pin 5 pin change interrupt
+#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
+#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 ADC_PRSCL 0x07 // clk/128
+
+// average drop across diode on this hardware
+#ifndef VOLTAGE_FUDGE_FACTOR
+#define VOLTAGE_FUDGE_FACTOR 4 // add 0.20V (measured 0.22V)
+#endif
+
+// this light has 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
+#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
+
+#define BUTTON_LED_PIN PA1 // pin 6
+#define BUTTON_LED_PORT PORTA // for all "PA" pins
+#define BUTTON_LED_DDR DDRA // for all "PA" pins
+#define BUTTON_LED_PUE PUEA // for all "PA" pins
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+// it also has an independent LED in the button
+#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
+
+
+inline void hwdef_setup() {
+ // enable output ports
+ // 3x7135
+ DDRC = (1 << CH2_PIN);
+ // 1x7135
+ DDRB = (1 << CH1_PIN);
+ // FET, aux R/G/B
+ DDRA = (1 << CH3_PIN)
+ | (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ | (1 << BUTTON_LED_PIN)
+ ;
+
+ // 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]: 1,0,1,0: PWM, Phase Correct, 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) | (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) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5)
+ ;
+
+ // WGM0[2:0]: 0,0,1: PWM, Phase Correct (DS table 11-8)
+ // CS0[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 11-9)
+ // COM0A[1:0]: 1,0: PWM OC0A in the normal direction (DS table 11-4)
+ // COM0B[1:0]: 0,0: OC0B disabled (DS table 11-7)
+ // TCCR0A: COM0A1, COM0A0, COM0B1, COM0B0, -, -, WGM01, WGM00
+ TCCR0A = (0<<WGM01) | (1<<WGM00) // PWM, Phase Correct, TOP=0xFF (DS table 11-5)
+ | (1<<COM1A1) | (0<<COM1A0) // PWM 0A in normal direction (DS table 11-4)
+ | (0<<COM1B1) | (0<<COM1B0) // PWM 0B disabled (DS table 11-7)
+ ;
+ // TCCR0B: FOC0A, FOC0B, -, -, WGM02, CS02, CS01, CS00
+ TCCR0B = (0<<CS02) | (0<<CS01) | (1<<CS00) // clk/1 (no prescaling) (DS table 11-9)
+ | (0<<WGM02) // PWM, Phase Correct, TOP=0xFF (DS table 11-8)
+ ;
+
+ // set PWM resolution
+ PWM_TOP = PWM_TOP_INIT;
+
+ // set up e-switch
+ SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch
+ SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-emisar-d4v2-nofet.c b/hwdef-emisar-d4v2-nofet.c
new file mode 100644
index 0000000..24477a7
--- /dev/null
+++ b/hwdef-emisar-d4v2-nofet.c
@@ -0,0 +1,55 @@
+// Emisar D4v2 (no DD FET, 1x7135 only) PWM helper functions
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "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() {
+ CH1_PWM = 0;
+ PWM_CNT = 0; // reset phase
+}
+
+// single set of LEDs with just a 1x7135 chip, max 350 mA or ~130 lm
+void set_level_main(uint8_t level) {
+ PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level);
+ // pulse frequency modulation, a.k.a. dynamic PWM
+ uint16_t top = PWM_GET16(pwm_tops, level);
+
+ CH1_PWM = ch1_pwm;
+ // wait to sync the counter and avoid flashes
+ while(actual_level && (PWM_CNT > (top - 32))) {}
+ PWM_TOP = top;
+ // force reset phase when turning on from zero
+ // (because otherwise the initial response is inconsistent)
+ if (! actual_level) PWM_CNT = 0;
+}
+
+bool gradual_tick_main(uint8_t gt) {
+ PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt);
+
+ GRADUAL_ADJUST_SIMPLE (pwm1, CH1_PWM);
+
+ if ( (pwm1 == CH1_PWM)
+ ) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
diff --git a/hwdef-emisar-d4v2.c b/hwdef-emisar-d4v2.c
new file mode 100644
index 0000000..026b30d
--- /dev/null
+++ b/hwdef-emisar-d4v2.c
@@ -0,0 +1,61 @@
+// Emisar D4v2 PWM helper functions
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "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() {
+ CH1_PWM = 0;
+ CH2_PWM = 0;
+ PWM_CNT = 0; // reset phase
+}
+
+// single set of LEDs with 2 stacked power channels, DDFET+1 or DDFET+linear
+void set_level_main(uint8_t level) {
+ PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level);
+ PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level);
+ // pulse frequency modulation, a.k.a. dynamic PWM
+ uint16_t top = PWM_GET16(pwm_tops, level);
+
+ CH1_PWM = ch1_pwm;
+ CH2_PWM = ch2_pwm;
+ // wait to sync the counter and avoid flashes
+ while(actual_level && (PWM_CNT > (top - 32))) {}
+ PWM_TOP = top;
+ // force reset phase when turning on from zero
+ // (because otherwise the initial response is inconsistent)
+ if (! actual_level) PWM_CNT = 0;
+}
+
+bool gradual_tick_main(uint8_t gt) {
+ PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt);
+ PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt);
+
+ GRADUAL_ADJUST_STACKED(pwm1, CH1_PWM, PWM_TOP_INIT);
+ GRADUAL_ADJUST_SIMPLE (pwm2, CH2_PWM);
+
+ if ( (pwm1 == CH1_PWM)
+ && (pwm2 == CH2_PWM)
+ ) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
diff --git a/hwdef-emisar-d4v2.h b/hwdef-emisar-d4v2.h
new file mode 100644
index 0000000..96c57a9
--- /dev/null
+++ b/hwdef-emisar-d4v2.h
@@ -0,0 +1,171 @@
+// hwdef for Emisar D4v2 (attiny1634)
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * Pin / Name / Function
+ * 1 PA6 FET PWM (direct drive) (PWM1B)
+ * 2 PA5 R: red aux LED (PWM0B)
+ * 3 PA4 G: green aux LED
+ * 4 PA3 B: blue aux LED
+ * 5 PA2 e-switch
+ * 6 PA1 button LED
+ * 7 PA0 (none)
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 (none) PWM0A
+ * 16 PB3 7135 PWM (PWM1A)
+ * 17 PB2 MISO
+ * 18 PB1 MOSI
+ * 19 PB0 (none)
+ * 20 PA7 (none)
+ * ADC12 thermal sensor
+ */
+
+#define ATTINY 1634
+#include <avr/io.h>
+
+#ifndef HWDEF_C_FILE
+#define HWDEF_C_FILE hwdef-emisar-d4v2.c
+#endif
+
+// allow using aux LEDs as extra channel modes
+#include "chan-rgbaux.h"
+
+// channel modes:
+// * 0. FET+7135 stacked
+// * 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
+// no args
+//#define USE_CHANNEL_MODE_ARGS
+//#define CHANNEL_MODE_ARGS 0,0,0,0,0,0,0,0
+
+
+#define PWM_CHANNELS 2 // old, remove this
+
+#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255
+#define PWM_GET PWM_GET8
+#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255)
+#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255
+#define PWM1_DATATYPE uint8_t // 1x7135 ramp
+#define PWM2_DATATYPE uint8_t // DD FET ramp
+
+// PWM parameters of both channels are tied together because they share a counter
+#define PWM_TOP ICR1 // holds the TOP value for for variable-resolution PWM
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+#define PWM_CNT TCNT1 // for dynamic PWM, reset phase
+
+// 1x7135 channel
+#define CH1_PIN PB3 // pin 16, 1x7135 PWM
+#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3
+
+// DD FET channel
+#define CH2_PIN PA6 // pin 1, DD FET PWM
+#define CH2_PWM OCR1B // OCR1B is the output compare register for PA6
+
+// e-switch
+#define SWITCH_PIN PA2 // pin 5
+#define SWITCH_PCINT PCINT2 // pin 5 pin change interrupt
+#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
+#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 ADC_PRSCL 0x07 // clk/128
+
+// average drop across diode on this hardware
+#ifndef VOLTAGE_FUDGE_FACTOR
+#define VOLTAGE_FUDGE_FACTOR 4 // add 0.20V (measured 0.22V)
+#endif
+
+// this light has 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
+#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
+
+#define BUTTON_LED_PIN PA1 // pin 6
+#define BUTTON_LED_PORT PORTA // for all "PA" pins
+#define BUTTON_LED_DDR DDRA // for all "PA" pins
+#define BUTTON_LED_PUE PUEA // for all "PA" pins
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+// it also has an independent LED in the button
+#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
+
+
+inline void hwdef_setup() {
+ // enable output ports
+ // 7135
+ DDRB = (1 << CH1_PIN);
+ // DD FET, aux R/G/B, button LED
+ DDRA = (1 << CH2_PIN)
+ | (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ | (1 << BUTTON_LED_PIN)
+ ;
+
+ // 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]: 1,0,1,0: PWM, Phase Correct, 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) | (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) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5)
+ ;
+
+ // set PWM resolution
+ PWM_TOP = PWM_TOP_INIT;
+
+ #if 0 // old 8-bit PWM setup
+ // 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
+ TCCR1A = (0<<WGM11) | (1<<WGM10) // 8-bit (TOP=0xFF) (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)
+ | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5)
+ ;
+ #endif
+
+ // set up e-switch
+ SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch
+ SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-FF_PL47.h b/hwdef-ff-pl47.h
index 5cd9684..7ce7070 100644
--- a/hwdef-FF_PL47.h
+++ b/hwdef-ff-pl47.h
@@ -1,9 +1,9 @@
-#ifndef HWDEF_FF_PL47_H
-#define HWDEF_FF_PL47_H
+// Fireflies PL47 driver layout
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
-/* Fireflies PL47 driver layout
- * same as a D4S, basically, except ...
- */
+// same as a D4S, basically, except ...
// ... the PL47 has aux LEDs on pin 7
#ifndef AUXLED_PIN
@@ -20,8 +20,7 @@
#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V
#endif
-#include "hwdef-Emisar_D4S.h"
+#include "hwdef-emisar-d4s.h"
#undef FSM_EMISAR_D4S_DRIVER
#undef FSM_EMISAR_D4_DRIVER
-#endif
diff --git a/hwdef-FF_ROT66.h b/hwdef-ff-rot66.h
index 7348ab2..3fea9f1 100644
--- a/hwdef-FF_ROT66.h
+++ b/hwdef-ff-rot66.h
@@ -1,9 +1,9 @@
-#ifndef HWDEF_FF_ROT66_H
-#define HWDEF_FF_ROT66_H
+// Fireflies ROT66 driver layout
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
-/* Fireflies ROT66 driver layout
- * same as a FW3A, basically, except ...
- */
+// same as a FW3A, basically, except ...
// ... except the ROT66 has a lighted button
#ifndef AUXLED_PIN
@@ -15,11 +15,10 @@
#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V
#endif
-#include "hwdef-FW3A.h"
+#include "hwdef-fw3a.h"
// ... and no optic nerve
#ifdef VISION_PIN
#undef VISION_PIN
#endif
-#endif
diff --git a/hwdef-fw3a.c b/hwdef-fw3a.c
new file mode 100644
index 0000000..b20969d
--- /dev/null
+++ b/hwdef-fw3a.c
@@ -0,0 +1,71 @@
+// BLF/TLF FW3A PWM helper functions
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+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
+ },
+};
+
+
+void set_level_zero() {
+ CH1_PWM = 0;
+ CH2_PWM = 0;
+ #ifdef CH3_PIN
+ CH3_PWM = 0;
+ #endif
+}
+
+// TODO: implement delta-sigma modulation for better low modes
+
+// single set of LEDs with 3 stacked power channels, FET+N+1
+// (or just use N+1 on the no-FET model or FET+1 model)
+void set_level_main(uint8_t level) {
+ PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level);
+ PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level);
+ #ifdef CH3_PIN
+ PWM_DATATYPE ch3_pwm = PWM_GET(pwm3_levels, level);
+ #endif
+
+ CH1_PWM = ch1_pwm;
+ CH2_PWM = ch2_pwm;
+ #ifdef CH3_PIN
+ CH3_PWM = ch3_pwm;
+ #endif
+}
+
+bool gradual_tick_main(uint8_t gt) {
+ PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt);
+ PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt);
+ #ifdef CH3_PIN
+ PWM_DATATYPE pwm3 = PWM_GET(pwm3_levels, gt);
+ #endif
+
+ GRADUAL_ADJUST_STACKED(pwm1, CH1_PWM, PWM_TOP_INIT);
+ #ifdef CH3_PIN
+ GRADUAL_ADJUST_STACKED(pwm2, CH2_PWM, PWM_TOP_INIT);
+ GRADUAL_ADJUST_SIMPLE (pwm3, CH3_PWM);
+ #else
+ GRADUAL_ADJUST_SIMPLE (pwm2, CH2_PWM);
+ #endif
+
+ if ( (pwm1 == CH1_PWM)
+ && (pwm2 == CH2_PWM)
+ #ifdef CH3_PIN
+ && (pwm3 == CH3_PWM)
+ #endif
+ ) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
diff --git a/hwdef-fw3a.h b/hwdef-fw3a.h
new file mode 100644
index 0000000..519fb97
--- /dev/null
+++ b/hwdef-fw3a.h
@@ -0,0 +1,105 @@
+// BLF/TLF FW3A driver layout
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * ----
+ * Reset -|1 8|- VCC
+ * eswitch -|2 7|- optic nerve
+ * FET -|3 6|- 7x7135
+ * GND -|4 5|- 1x7135
+ * ----
+ */
+
+#define ATTINY 85
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-fw3a.c
+
+// channel modes
+// * 0. FET+7+1 stacked
+#define NUM_CHANNEL_MODES 1
+enum CHANNEL_MODES {
+ CM_MAIN = 0,
+};
+
+#define DEFAULT_CHANNEL_MODE CM_MAIN
+
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b00000001
+
+
+#define PWM_CHANNELS 3 // old, remove this
+
+#define PWM_BITS 8 // attiny85 only supports up to 8 bits
+#define PWM_GET PWM_GET8
+#define PWM_DATATYPE uint8_t
+#define PWM_DATATYPE2 uint16_t
+#define PWM1_DATATYPE uint8_t // 1x7135 ramp
+#define PWM2_DATATYPE uint8_t // 7x7135 ramp
+#define PWM3_DATATYPE uint8_t // DD FET ramp
+
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+
+// 1x7135 channel
+#define CH1_PIN PB0 // pin 5, 1x7135 PWM
+#define CH1_PWM OCR0A // OCR0A is the output compare register for PB0
+
+// 7x7135 channel
+#define CH2_PIN PB1 // pin 6, 7x7135 PWM
+#define CH2_PWM OCR0B // OCR0B is the output compare register for PB1
+
+// DD FET channel
+#define CH3_PIN PB4 // pin 3, FET PWM
+#define CH3_PWM OCR1B // OCR1B is the output compare register for PB4
+
+// e-switch
+#ifndef SWITCH_PIN
+#define SWITCH_PIN PB3 // pin 2
+#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt
+#endif
+
+#ifndef VISION_PIN
+#define VISION_PIN PB2 // pin 7, optic nerve
+//#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
+//#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
+#endif
+#define ADC_PRSCL 0x07 // clk/128
+
+// average drop across diode on this hardware
+#ifndef VOLTAGE_FUDGE_FACTOR
+#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V
+#endif
+
+#define FAST 0xA3 // fast PWM both channels
+#define PHASE 0xA1 // phase-correct PWM both channels
+
+
+inline void hwdef_setup() {
+
+ // configure PWM channels
+ DDRB = (1 << CH1_PIN)
+ | (1 << CH2_PIN)
+ | (1 << CH3_PIN);
+
+ // configure PWM channels
+ TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
+ TCCR0A = PHASE;
+
+ // Second PWM counter is ... weird
+ TCCR1 = _BV (CS10);
+ GTCCR = _BV (COM1B1) | _BV (PWM1B);
+ OCR1C = PWM_TOP_INIT; // Set ceiling value to maximum
+
+ // configure e-switch
+ PORTB = (1 << SWITCH_PIN); // e-switch is the only input
+ PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin
+
+ // TODO: set up the vision pin
+
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-fw3x-lume1.c b/hwdef-fw3x-lume1.c
new file mode 100644
index 0000000..2f31ed0
--- /dev/null
+++ b/hwdef-fw3x-lume1.c
@@ -0,0 +1,60 @@
+// FW3X Lume1 PWM helper functions
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#include "chan-rgbaux.c"
+
+void set_level_zero();
+
+void set_level_main(uint8_t level);
+bool gradual_tick_main(uint8_t gt);
+
+
+Channel channels[] = {
+ { // channel 1 only
+ .set_level = set_level_main,
+ .gradual_tick = gradual_tick_main
+ },
+ RGB_AUX_CHANNELS
+};
+
+
+void set_level_zero() {
+ CH1_PWM = 0;
+ CH2_PWM = 0;
+ PWM_CNT = 0; // reset phase
+ CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable regulator
+}
+
+// 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
+
+ PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level);
+ PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level);
+
+ CH1_PWM = ch1_pwm;
+ CH2_PWM = ch2_pwm;
+
+ // force reset phase when turning on from zero
+ // (for faster, more consistent initial response)
+ if (! actual_level) PWM_CNT = 0;
+}
+
+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;
+ }
+
+ PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt);
+ GRADUAL_ADJUST_SIMPLE(pwm1, CH1_PWM);
+
+ if (pwm1 == CH1_PWM) return true; // done
+ return false; // not done yet
+}
+
diff --git a/hwdef-fw3x-lume1.h b/hwdef-fw3x-lume1.h
index 09c5bfe..c03248b 100644
--- a/hwdef-fw3x-lume1.h
+++ b/hwdef-fw3x-lume1.h
@@ -1,14 +1,16 @@
-#ifndef HWDEF_FW3X_LUME1_H
-#define HWDEF_FW3X_LUME1_H
+// lume1 Driver Rev B for FW3x driver layout (attiny1634)
+// Copyright (C) 2020-2023 LoneOceans, Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
/* lume1 Driver Rev B for FW3x driver layout (attiny1634)
* www.loneoceans.com/labs/ for more information
*
* Pin / Name / Function in Lume1 Rev B
* 1 PA6 Regulated PWM (PWM1B)
- * 2 PA5 R red aux LED (PWM0B)
- * 3 PA4 G green aux LED
- * 4 PA3 B blue aux LED
+ * 2 PA5 R: red aux LED (PWM0B)
+ * 3 PA4 G: green aux LED
+ * 4 PA3 B: blue aux LED
* 5 PA2 e-switch (PCINT2)
* 6 PA1 Jumper 1
* 7 PA0 Jumper 2
@@ -28,36 +30,57 @@
* ADC12 internal thermal sensor (not used for lume1)
*
* Main LED power uses one pin as a global Buck Boost Enable, and
- * one pin to control the power level via PWM. Another pin is used
- * for FET control.
+ * one pin to control the power level via PWM.
+ * Another pin is used for DD FET control.
*/
-#ifdef ATTINY
-#undef ATTINY
-#endif
#define ATTINY 1634
#include <avr/io.h>
-#define PWM_CHANNELS 2
-// Added for Lume1 Buck Boost Driver
-#define PWM_BITS 10 // 0 to 1023 at 3.9 kHz, not 0 to 255 at 15.6 kHz
-#define PWM_TOP 1023
+#define HWDEF_C_FILE hwdef-fw3x-lume1.c
-#define SWITCH_PIN PA2 // pin 5
-#define SWITCH_PCINT PCINT2 // pin 5 pin change interrupt
-#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
-#define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0]
-#define SWITCH_PORT PINA // PINA or PINB or PINC
+// allow using aux LEDs as extra channel modes
+#include "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
-#define PWM1_PIN PA6 // pin 1, Buck Boost CTRL pin or 7135-eqv PWM
-#define PWM1_LVL OCR1B // OCR1A is the output compare register for PA6
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b0000000000000001
-#define PWM2_PIN PB3 // pin 16, FET PWM Pin, but only used as on (1023) or off (0)
-#define PWM2_LVL OCR1A // OCR1A is the output compare register for PB3
+
+#define PWM_CHANNELS 2 // old, remove this
// Added for Lume1 Buck Boost Driver
-#define LED_ENABLE_PIN PA7 // pin 20, BuckBoost Enable
-#define LED_ENABLE_PORT PORTA // control port for PA7
+#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_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
+
+// 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
+
+// regulated channel
+#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)
+#define CH2_PWM OCR1A // OCR1A is the output compare register for PB3
/* // For Jumpers X1 to X4, no SW support yet
#define JUMPER1_PIN PA1
@@ -66,8 +89,17 @@
#define JUMPER4_PIN PC4
*/
-#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened
-#define VOLTAGE_PIN PB0 // Pin 19 PB0 ADC5
+// e-switch
+#define SWITCH_PIN PA2 // pin 5
+#define SWITCH_PCINT PCINT2 // pin 5 pin change interrupt
+#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
+#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 PB0 // Pin 19 PB0 ADC5
// pin to ADC mappings are in DS table 19-4
#define VOLTAGE_ADC ADC5D // digital input disable pin for PB1
// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6
@@ -117,53 +149,54 @@
#define AUXLED_RGB_DDR DDRA // DDRA or DDRB or DDRC
#define AUXLED_RGB_PUE PUEA // PUEA or PUEB or PUEC
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
// For lume1 driver, no SW support for Auxillary Jumpers X1 to X4 yet!
inline void hwdef_setup() {
- // enable output ports in Data Direction Registers
- // FET PWM Pin
- DDRB = (1 << PWM2_PIN);
- // Main PWM, Buck Boost Enable Pin, aux R/G/B
- DDRA = (1 << PWM1_PIN)
- | (1 << LED_ENABLE_PIN)
- | (1 << AUXLED_R_PIN)
- | (1 << AUXLED_G_PIN)
- | (1 << AUXLED_B_PIN)
- ;
- //DDRB&=~(1<<VOLTAGE_PIN); // All pins are input by default
- /* // For Jumpers X1 to X4, no SW support yet
- DDRA &= (1<<JUMPER1_PIN);
- DDRA &= (1<<JUMPER2_PIN);
- DDRC &= (1<<JUMPER3_PIN);
- DDRC &= (1<<JUMPER4_PIN);
- PUEA = (1 << JUMPER1_PIN);
- PUEA = (1 << JUMPER2_PIN);
- PUEC = (1 << JUMPER3_PIN);
- PUEC = (1 << JUMPER4_PIN);
- */
-
- // configure PWM for 10 bit at 3.9kHz
- // 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)
- // 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
- ;
-
- // set up e-switch
- //PORTA = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC?
- PUEA = (1 << SWITCH_PIN); // pull-up for e-switch
- SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
+ // enable output ports
+ // FET PWM Pin
+ DDRB = (1 << CH2_PIN);
+ // Main PWM, Buck Boost Enable Pin, aux R/G/B
+ DDRA = (1 << CH1_PIN)
+ | (1 << CH1_ENABLE_PIN)
+ | (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ ;
+
+ //DDRB&=~(1<<VOLTAGE_PIN); // All pins are input by default
+ /* // For Jumpers X1 to X4, no SW support yet
+ DDRA &= (1<<JUMPER1_PIN);
+ DDRA &= (1<<JUMPER2_PIN);
+ DDRC &= (1<<JUMPER3_PIN);
+ DDRC &= (1<<JUMPER4_PIN);
+ PUEA = (1 << JUMPER1_PIN);
+ PUEA = (1 << JUMPER2_PIN);
+ PUEC = (1 << JUMPER3_PIN);
+ PUEC = (1 << JUMPER4_PIN);
+ */
+
+ // configure PWM for 10 bit at 3.9kHz
+ // 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)
+ // 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
+ ;
+
+ // set PWM resolution
+ PWM_TOP = PWM_TOP_INIT;
+
+ // set up e-switch
+ SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch
+ SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
}
+
#define LAYOUT_DEFINED
-#endif
diff --git a/hwdef-gchart-fet1-t1616.h b/hwdef-gchart-fet1-t1616.h
index 2435b99..2d2b7a6 100644
--- a/hwdef-gchart-fet1-t1616.h
+++ b/hwdef-gchart-fet1-t1616.h
@@ -1,70 +1,92 @@
-#ifndef HWDEF_GCH_FET1_T1616_H
-#define HWDEF_GCH_FET1_T1616_H
+// gChart's custom FET+1 driver layout
+// Copyright (C) 2020-2023 gchart, Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * PB0 - PWM for FET (TCA - WO0)
+ * PB1 - PWM for 7135 (TCA - WO1)
+ * PB2 - Switch pin, internal pullup
+ * PB3 - Aux LED with 4700 Ohm series resistor
+ * Read voltage from VCC pin, has diode with ~0.4v drop
+*/
-/* gChart's custom FET+1 driver layout
+#define ATTINY 1616
+#include <avr/io.h>
-PB0 - PWM for FET (TCA - WO0)
-PB1 - PWM for 7135 (TCA - WO1)
-PB2 - Switch pin, internal pullup
-PB3 - Aux LED with 4700 Ohm series resistor
-Read voltage from VCC pin, has diode with ~0.4v drop
+// 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 hwdef-wurkkos-ts10.c
-*/
+// allow using aux LEDs as extra channel modes
+#include "chan-aux.h"
+// channel modes:
+// * 0. FET+7135 stacked
+// * 1. aux LEDs
+#define NUM_CHANNEL_MODES 2
+enum CHANNEL_MODES {
+ CM_MAIN = 0,
+ CM_AUX
+};
-#define LAYOUT_DEFINED
+#define DEFAULT_CHANNEL_MODE CM_MAIN
-#ifdef ATTINY
-#undef ATTINY
-#endif
-#define ATTINY 1616
-#include <avr/io.h>
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b00000001
-#define PWM_CHANNELS 2
-#ifndef SWITCH_PIN
-#define SWITCH_PIN PIN2_bp
-#define SWITCH_PORT VPORTB.IN
-#define SWITCH_ISC_REG PORTB.PIN2CTRL
-#define SWITCH_VECT PORTB_PORT_vect
-#define SWITCH_INTFLG VPORTB.INTFLAGS
-#endif
+#define PWM_CHANNELS 2 // old, remove this
+#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255
+#define PWM_GET PWM_GET8
+#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255)
+#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255
+#define PWM1_DATATYPE uint8_t // 1x7135 ramp
+#define PWM2_DATATYPE uint8_t // DD FET ramp
-// 7135 channel
-#ifndef PWM1_PIN
-#define PWM1_PIN PB1 //
-#define PWM1_LVL TCA0.SINGLE.CMP1 // CMP1 is the output compare register for PB1
-#endif
+// PWM parameters of both channels are tied together because they share a counter
+#define PWM_TOP TCA0.SINGLE.PERBUF // holds the TOP value for for variable-resolution PWM
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+// not necessary when double-buffered "BUF" registers are used
+#define PWM_CNT TCA0.SINGLE.CNT // for resetting phase after each TOP adjustment
-// FET channel
-#ifndef PWM2_PIN
-#define PWM2_PIN PB0 //
-#define PWM2_LVL TCA0.SINGLE.CMP0 // CMP0 is the output compare register for PB0
-#endif
+// 1x7135 channel
+#define CH1_PIN PB1
+#define CH1_PWM TCA0.SINGLE.CMP1BUF // CMP1 is the output compare register for PB1
+
+// DD FET channel
+#define CH2_PIN PB0
+#define CH2_PWM TCA0.SINGLE.CMP0BUF // CMP0 is the output compare register for PB0
+
+// e-switch
+#define SWITCH_PIN PIN2_bp
+#define SWITCH_PORT VPORTB.IN
+#define SWITCH_ISC_REG PORTB.PIN2CTRL
+#define SWITCH_VECT PORTB_PORT_vect
+#define SWITCH_INTFLG VPORTB.INTFLAGS
// average drop across diode on this hardware
#ifndef VOLTAGE_FUDGE_FACTOR
#define VOLTAGE_FUDGE_FACTOR 8 // 4 = add 0.20V
#endif
-
// lighted button
-#ifndef AUXLED_PIN
-#define AUXLED_PIN PIN3_bp
-#define AUXLED_PORT PORTB
-#endif
+#define AUXLED_PIN PIN3_bp
+#define AUXLED_PORT PORTB
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
inline void hwdef_setup() {
// set up the system clock to run at 10 MHz instead of the default 3.33 MHz
- _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm );
+ _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB,
+ CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm );
//VPORTA.DIR = 0b00000010;
- VPORTB.DIR = PIN0_bm | PIN1_bm | PIN3_bm;
+ // Outputs
+ VPORTB.DIR = PIN0_bm // DD FET
+ | PIN1_bm // 7135
+ | PIN3_bm; // Aux LED
//VPORTC.DIR = 0b00000000;
// enable pullups on the input pins to reduce power
@@ -97,10 +119,16 @@ inline void hwdef_setup() {
// For Fast (Single Slope) PWM use TCA_SINGLE_WGMODE_SINGLESLOPE_gc
// For Phase Correct (Dual Slope) PWM use TCA_SINGLE_WGMODE_DSBOTTOM_gc
// See the manual for other pins, clocks, configs, portmux, etc
- TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm | TCA_SINGLE_CMP1EN_bm | TCA_SINGLE_WGMODE_DSBOTTOM_gc;
- TCA0.SINGLE.PER = 255;
- TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc | TCA_SINGLE_ENABLE_bm;
+ TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm
+ | TCA_SINGLE_CMP1EN_bm
+ | TCA_SINGLE_WGMODE_DSBOTTOM_gc;
+ TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc
+ | TCA_SINGLE_ENABLE_bm;
+
+ PWM_TOP = PWM_TOP_INIT;
+
}
-#endif
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-mateminco-mf01-mini.h b/hwdef-mateminco-mf01-mini.h
new file mode 100644
index 0000000..35405db
--- /dev/null
+++ b/hwdef-mateminco-mf01-mini.h
@@ -0,0 +1,100 @@
+// Mateminco MF01-Mini driver layout
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * ----
+ * Reset -|1 8|- VCC
+ * eswitch -|2 7|- aux LEDs
+ * FET PWM -|3 6|- PWM (7x7135)
+ * GND -|4 5|- PWM (1x7135)
+ * ----
+ */
+
+#define ATTINY 85
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-fw3a.c
+
+// channel modes
+// * 0. FET+N+1 stacked
+#define NUM_CHANNEL_MODES 1
+enum CHANNEL_MODES {
+ CM_MAIN = 0,
+};
+
+#define DEFAULT_CHANNEL_MODE CM_MAIN
+
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b00000001
+
+
+#define PWM_CHANNELS 3 // old, remove this
+
+#define PWM_BITS 8 // attiny85 only supports up to 8 bits
+#define PWM_GET PWM_GET8
+#define PWM_DATATYPE uint8_t
+#define PWM_DATATYPE2 uint16_t
+#define PWM1_DATATYPE uint8_t // 1x7135 ramp
+#define PWM2_DATATYPE uint8_t // 7x7135 ramp
+#define PWM3_DATATYPE uint8_t // DD FET ramp
+
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+
+// 1x7135 channel
+#define CH1_PIN PB0 // pin 5, 1x7135 PWM
+#define CH1_PWM OCR0A // OCR0A is the output compare register for PB0
+
+// 7x7135 channel
+#define CH2_PIN PB1 // pin 6, 7x7135 PWM
+#define CH2_PWM OCR0B // OCR0B is the output compare register for PB1
+
+// DD FET channel
+#define CH3_PIN PB4 // pin 3, FET PWM
+#define CH3_PWM OCR1B // OCR1B is the output compare register for PB4
+
+// lighted button and 1-channel front aux
+#define AUXLED_PIN PB2 // pin 7
+
+// e-switch
+#ifndef SWITCH_PIN
+#define SWITCH_PIN PB3 // pin 2
+#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt
+#endif
+
+#define ADC_PRSCL 0x07 // clk/128
+
+// average drop across diode on this hardware
+#ifndef VOLTAGE_FUDGE_FACTOR
+#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V
+#endif
+
+#define FAST 0xA3 // fast PWM both channels
+#define PHASE 0xA1 // phase-correct PWM both channels
+
+
+inline void hwdef_setup() {
+
+ // configure PWM channels
+ DDRB = (1 << CH1_PIN)
+ | (1 << CH2_PIN)
+ | (1 << CH3_PIN);
+
+ // configure PWM channels
+ TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
+ TCCR0A = PHASE;
+
+ // Second PWM counter is ... weird
+ TCCR1 = _BV (CS10);
+ GTCCR = _BV (COM1B1) | _BV (PWM1B);
+ OCR1C = PWM_TOP_INIT; // Set ceiling value to maximum
+
+ // configure e-switch
+ PORTB = (1 << SWITCH_PIN); // e-switch is the only input
+ PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-mateminco-mf01s.h b/hwdef-mateminco-mf01s.h
new file mode 100644
index 0000000..af214b2
--- /dev/null
+++ b/hwdef-mateminco-mf01s.h
@@ -0,0 +1,102 @@
+// Mateminco MT18S / Astrolux MF01S driver layout
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * ----
+ * Reset -|1 8|- VCC (unused)
+ * eswitch -|2 7|- Voltage divider (2S)
+ * AUX LED -|3 6|- PWM (FET)
+ * GND -|4 5|- PWM (smaller FET)
+ * ----
+ */
+
+#define ATTINY 85
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-emisar-d4.c
+
+// channel modes
+// * 0. small FET + big FET stacked
+#define NUM_CHANNEL_MODES 1
+enum CHANNEL_MODES {
+ CM_MAIN = 0,
+};
+
+#define DEFAULT_CHANNEL_MODE CM_MAIN
+
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b00000001
+
+
+#define PWM_CHANNELS 2 // old, remove this
+
+#define PWM_BITS 8 // attiny85 only supports up to 8 bits
+#define PWM_GET PWM_GET8
+#define PWM_DATATYPE uint8_t
+#define PWM_DATATYPE2 uint16_t
+#define PWM1_DATATYPE uint8_t // little FET ramp
+#define PWM2_DATATYPE uint8_t // big FET ramp
+
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+
+// little FET channel
+#define CH1_PIN PB0 // pin 5, 1x7135 PWM
+#define CH1_PWM OCR0A // OCR0A is the output compare register for PB0
+
+// big FET channel
+#define CH2_PIN PB1 // pin 6, FET PWM
+#define CH2_PWM OCR0B // OCR0B is the output compare register for PB1
+
+// lighted button and 1-channel front aux
+#define AUXLED_PIN PB4 // pin 3
+
+// e-switch
+#ifndef SWITCH_PIN
+#define SWITCH_PIN PB3 // pin 2
+#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt
+#endif
+
+#define USE_VOLTAGE_DIVIDER // use a voltage divider on pin 7, not VCC
+#ifndef VOLTAGE_PIN
+#define VOLTAGE_PIN PB2 // pin 7, voltage ADC
+#define VOLTAGE_CHANNEL 0x01 // MUX 01 corresponds with PB2
+#define VOLTAGE_ADC ADC1D // Digital input disable bit corresponding with PB2
+// inherited from tk-attiny.h
+//#define VOLTAGE_ADC_DIDR DIDR0 // DIDR for ADC1
+// 1.1V reference, left-adjust, ADC1/PB2
+//#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)
+#endif
+#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)
+#endif
+#ifndef ADC_22
+#define ADC_22 (117*4)
+#endif
+
+#define FAST 0xA3 // fast PWM both channels
+#define PHASE 0xA1 // phase-correct PWM both channels
+
+
+inline void hwdef_setup() {
+ // configure PWM channels
+ DDRB = (1 << CH1_PIN)
+ | (1 << CH2_PIN);
+
+ TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
+ TCCR0A = PHASE;
+
+ // configure e-switch
+ PORTB = (1 << SWITCH_PIN); // e-switch is the only input
+ PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-mateminco-mt35-mini.h b/hwdef-mateminco-mt35-mini.h
new file mode 100644
index 0000000..015c813
--- /dev/null
+++ b/hwdef-mateminco-mt35-mini.h
@@ -0,0 +1,93 @@
+// Mateminco MT35-Mini / Astrolux FT03
+// Copyright (C) 2022-2023 (original author TBD), Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * ----
+ * Reset -|1 8|- VCC
+ * eswitch -|2 7|- Aux LED
+ * 1x7135 -|3 6|- NC
+ * GND -|4 5|- FET
+ * ----
+ */
+
+#define ATTINY 85
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-emisar-d4.c
+
+// channel modes
+// * 0. FET+7135 stacked
+#define NUM_CHANNEL_MODES 1
+enum CHANNEL_MODES {
+ CM_MAIN = 0,
+};
+
+#define DEFAULT_CHANNEL_MODE CM_MAIN
+
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b00000001
+
+
+#define PWM_CHANNELS 2 // old, remove this
+
+#define PWM_BITS 8 // attiny85 only supports up to 8 bits
+#define PWM_GET PWM_GET8
+#define PWM_DATATYPE uint8_t
+#define PWM_DATATYPE2 uint16_t
+#define PWM1_DATATYPE uint8_t // little FET ramp
+#define PWM2_DATATYPE uint8_t // big FET ramp
+
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+
+// 1x7135 channel
+#define CH1_PIN PB4 // pin 3, 1x7135 PWM
+#define CH1_PWM OCR1B // OCR1B is the output compare register for PB4
+
+// DD FET channel
+#define CH2_PIN PB0 // pin 5, FET PWM
+#define CH2_PWM OCR0A // OCR0A is the output compare register for PB0
+
+// lighted button
+#define AUXLED_PIN PB2 // pin 7
+
+// e-switch
+#ifndef SWITCH_PIN
+#define SWITCH_PIN PB3 // pin 2
+#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt
+#endif
+
+#define ADC_PRSCL 0x07 // clk/128
+
+// average drop across diode on this hardware
+#ifndef VOLTAGE_FUDGE_FACTOR
+#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V
+#endif
+
+#define FAST 0xA3 // fast PWM both channels
+#define PHASE 0xA1 // phase-correct PWM both channels
+
+
+inline void hwdef_setup() {
+ // configure PWM channels
+ DDRB = (1 << CH1_PIN)
+ | (1 << CH2_PIN);
+
+ // configure PWM channels
+ TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
+ TCCR0A = PHASE;
+
+ // Second PWM counter is ... weird
+ TCCR1 = _BV (CS10);
+ GTCCR = _BV (COM1B1) | _BV (PWM1B);
+ OCR1C = PWM_TOP_INIT; // Set ceiling value to maximum
+
+ // configure e-switch
+ PORTB = (1 << SWITCH_PIN); // e-switch is the only input
+ PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-noctigon-dm11-boost.c b/hwdef-noctigon-dm11-boost.c
new file mode 100644
index 0000000..932323a
--- /dev/null
+++ b/hwdef-noctigon-dm11-boost.c
@@ -0,0 +1,97 @@
+// Noctigon DM11 (boost driver) PWM helper functions
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#include "chan-rgbaux.c"
+
+
+void set_level_zero();
+
+void set_level_main(uint8_t level);
+bool gradual_tick_main(uint8_t gt);
+
+
+Channel channels[] = {
+ { // channel 1 only
+ .set_level = set_level_main,
+ .gradual_tick = gradual_tick_main
+ },
+ RGB_AUX_CHANNELS
+};
+
+
+void set_level_zero() {
+ // 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;
+ PWM_CNT = 0; // reset phase
+ CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN ); // disable opamp
+ CH1_ENABLE_PORT2 &= ~(1 << CH1_ENABLE_PIN2); // disable PMIC
+}
+
+// single set of LEDs with single power channel, boost
+void set_level_main(uint8_t level) {
+ PWM_DATATYPE ch1 = PWM_GET(pwm1_levels, level);
+
+ // set delta-sigma soft levels
+ ch1_dsm_lvl = ch1;
+
+ // set hardware PWM levels and init dsm loop
+ CH1_PWM = ch1_pwm = ch1 >> 7;
+
+ // enable timer overflow interrupt so DSM can work
+ DSM_INTCTRL |= DSM_OVF_bm;
+
+ // force reset phase when turning on from zero
+ // (because otherwise the initial response is inconsistent)
+ if (! actual_level) PWM_CNT = 0;
+
+ CH1_ENABLE_PORT |= (1 << CH1_ENABLE_PIN ); // enable opamp
+ CH1_ENABLE_PORT2 |= (1 << CH1_ENABLE_PIN2); // enable PMIC
+}
+
+// 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) {
+ PWM_DATATYPE ch1 = PWM_GET(pwm1_levels, gt);
+
+ // 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
+}
+
diff --git a/hwdef-noctigon-dm11-boost.h b/hwdef-noctigon-dm11-boost.h
new file mode 100644
index 0000000..d56a5f5
--- /dev/null
+++ b/hwdef-noctigon-dm11-boost.h
@@ -0,0 +1,206 @@
+// Noctigon DM11 boost driver layout (attiny1634)
+// Copyright (C) 2021-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * (based on Noctigon K1)
+ *
+ * Pin / Name / Function
+ * 1 PA6 (none) (PWM1B) (reserved for DD drivers)
+ * 2 PA5 R: red aux LED (PWM0B)
+ * 3 PA4 G: green aux LED
+ * 4 PA3 B: blue aux LED
+ * 5 PA2 L: button LED
+ * 6 PA1 (none)
+ * 7 PA0 (none)
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 boost PMIC enable (PWM0A not used)
+ * 16 PB3 main LED PWM (PWM1A)
+ * 17 PB2 MISO (PCINT10)
+ * 18 PB1 MOSI / battery voltage (ADC6)
+ * 19 PB0 Opamp power
+ * 20 PA7 e-switch (PCINT7)
+ * ADC12 thermal sensor
+ *
+ * Main LED power uses one pin to turn the Opamp on/off,
+ * and one pin to control Opamp power level.
+ * Regulated brightness control uses the power level pin, with PWM+DSM.
+ * The on/off pin is only used to turn the main LED on and off,
+ * not to change brightness.
+ */
+
+#define ATTINY 1634
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-noctigon-dm11-boost.c
+
+// allow using aux LEDs as extra channel modes
+#include "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
+// no args
+//#define USE_CHANNEL_MODE_ARGS
+//#define CHANNEL_MODE_ARGS 0,0,0,0,0,0,0,0
+
+
+#define PWM_CHANNELS 1 // old, remove this
+
+#define PWM_BITS 16 // 0 to 32640 (0 to 255 PWM + 0 to 127 DSM) at constant kHz
+#define PWM_GET PWM_GET16
+#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 // 15-bit PWM+DSM ramp
+
+#define PWM_TOP ICR1 // holds the TOP value for variable-resolution PWM
+#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 90 // less time in delay() because more time spent in interrupts
+
+// regulated channel
+uint16_t ch1_dsm_lvl;
+uint8_t ch1_pwm, ch1_dsm;
+#define CH1_PIN PB3 // pin 16, Opamp reference
+#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3
+
+#define CH1_ENABLE_PIN PB0 // pin 19, Opamp power
+#define CH1_ENABLE_PORT PORTB // control port for PB0
+
+#define CH1_ENABLE_PIN2 PC0 // pin 15, boost PMIC enable
+#define CH1_ENABLE_PORT2 PORTC // control port for PC0
+
+// e-switch
+#define SWITCH_PIN PA7 // pin 20
+#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt
+#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
+#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
+
+// this light has 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
+#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
+
+#define BUTTON_LED_PIN PA2 // pin 5
+#define BUTTON_LED_PORT PORTA // for all "PA" pins
+#define BUTTON_LED_DDR DDRA // for all "PA" pins
+#define BUTTON_LED_PUE PUEA // for all "PA" pins
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+// it also has an independent LED in the button
+#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
+
+
+inline void hwdef_setup() {
+ // enable output ports
+ // boost PMIC on/off
+ DDRC = (1 << CH1_ENABLE_PIN2);
+ // Opamp level and Opamp on/off
+ DDRB = (1 << CH1_PIN)
+ | (1 << CH1_ENABLE_PIN);
+ // aux R/G/B, button LED
+ DDRA = (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ | (1 << BUTTON_LED_PIN)
+ ;
+
+ // 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
+ // 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) | (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
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-noctigon-dm11.h b/hwdef-noctigon-dm11.h
new file mode 100644
index 0000000..b35b27f
--- /dev/null
+++ b/hwdef-noctigon-dm11.h
@@ -0,0 +1,183 @@
+// Noctigon DM11 driver layout (attiny1634)
+// Copyright (C) 2021-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * (based on Noctigon K1)
+ *
+ * Pin / Name / Function
+ * 1 PA6 FET PWM (direct drive) (PWM1B) (on some models)
+ * 2 PA5 R: red aux LED (PWM0B)
+ * 3 PA4 G: green aux LED
+ * 4 PA3 B: blue aux LED
+ * 5 PA2 L: button LED (on some models)
+ * 6 PA1 (none)
+ * 7 PA0 (none)
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 (none) PWM0A
+ * 16 PB3 main LED PWM (linear) (PWM1A)
+ * 17 PB2 MISO / (none) (PCINT10)
+ * 18 PB1 MOSI / battery voltage (ADC6)
+ * 19 PB0 Opamp power
+ * 20 PA7 e-switch (PCINT7)
+ * ADC12 thermal sensor
+ *
+ * Main LED power uses one pin to turn the Opamp on/off,
+ * and one pin to control Opamp power level.
+ * Linear brightness control uses the power level pin, with dynamic PWM.
+ * The on/off pin is only used to turn the main LED on and off,
+ * not to change brightness.
+ * Some models also have a direct-drive FET for turbo.
+ */
+
+#define ATTINY 1634
+#include <avr/io.h>
+
+#ifndef HWDEF_C_FILE
+#define HWDEF_C_FILE hwdef-noctigon-kr4.c
+#endif
+
+// allow using aux LEDs as extra channel modes
+#include "chan-rgbaux.h"
+
+// channel modes:
+// * 0. linear + DD FET stacked
+// * 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
+// no args
+//#define USE_CHANNEL_MODE_ARGS
+//#define CHANNEL_MODE_ARGS 0,0,0,0,0,0,0,0
+
+
+#define PWM_CHANNELS 2 // old, remove this
+
+#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255
+#define PWM_GET PWM_GET8
+#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255)
+#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255
+#define PWM1_DATATYPE uint8_t // linear ramp
+#define PWM2_DATATYPE uint8_t // DD FET ramp
+
+// PWM parameters of both channels are tied together because they share a counter
+#define PWM_TOP ICR1 // holds the TOP value for variable-resolution PWM
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+#define PWM_CNT TCNT1 // for dynamic PWM, reset phase
+
+// linear channel
+#define CH1_PIN PB3 // pin 16, Opamp reference
+#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3
+#define CH1_ENABLE_PIN PB0 // pin 19, Opamp power
+#define CH1_ENABLE_PORT PORTB // control port for PB0
+
+// DD FET channel
+#define CH2_PIN PA6 // pin 1, DD FET PWM
+#define CH2_PWM OCR1B // OCR1B is the output compare register for PA6
+
+// e-switch
+#define SWITCH_PIN PA7 // pin 20
+#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt
+#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
+#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
+
+// this light has 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
+#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
+
+#define BUTTON_LED_PIN PA2 // pin 5
+#define BUTTON_LED_PORT PORTA // for all "PA" pins
+#define BUTTON_LED_DDR DDRA // for all "PA" pins
+#define BUTTON_LED_PUE PUEA // for all "PA" pins
+
+inline void hwdef_setup() {
+ // enable output ports
+ // Opamp level and Opamp on/off
+ DDRB = (1 << CH1_PIN)
+ | (1 << CH1_ENABLE_PIN);
+ // DD FET PWM, aux R/G/B, button LED
+ DDRA = (1 << CH2_PIN)
+ | (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ | (1 << BUTTON_LED_PIN)
+ ;
+
+ // 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]: 1,0,1,0: PWM, Phase Correct, 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) | (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) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5)
+ ;
+
+ // set PWM resolution
+ PWM_TOP = PWM_TOP_INIT;
+
+ // set up e-switch
+ SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch
+ SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-noctigon-k1-boost.h b/hwdef-noctigon-k1-boost.h
new file mode 100644
index 0000000..a2e693a
--- /dev/null
+++ b/hwdef-noctigon-k1-boost.h
@@ -0,0 +1,188 @@
+// Noctigon K1 12V driver layout (attiny1634)
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * Pin / Name / Function
+ * 1 PA6 (none) (PWM1B) (reserved for DD drivers)
+ * 2 PA5 R: red aux LED (PWM0B)
+ * 3 PA4 G: green aux LED
+ * 4 PA3 B: blue aux LED
+ * 5 PA2 (none) (reserved for L: button LED (on some models))
+ * 6 PA1 (none)
+ * 7 PA0 (none)
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 boost PMIC enable (PWM0A not used)
+ * 16 PB3 main LED PWM (PWM1A)
+ * 17 PB2 MISO
+ * 18 PB1 MOSI / battery voltage (ADC6)
+ * 19 PB0 Opamp power
+ * 20 PA7 e-switch (PCINT7)
+ * ADC12 thermal sensor
+ *
+ * Main LED power uses one pin to turn the Opamp on/off,
+ * and one pin to control Opamp power level.
+ * Regulated brightness control uses the power level pin, with PWM+DSM.
+ * The on/off pin is only used to turn the main LED on and off,
+ * not to change brightness.
+ */
+
+#define ATTINY 1634
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-noctigon-dm11-boost.c
+
+// allow using aux LEDs as extra channel modes
+#include "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
+// no args
+//#define USE_CHANNEL_MODE_ARGS
+//#define CHANNEL_MODE_ARGS 0,0,0,0,0,0,0,0
+
+
+#define PWM_CHANNELS 1 // old, remove this
+
+#define PWM_BITS 16 // 0 to 32640 (0 to 255 PWM + 0 to 127 DSM) at constant kHz
+#define PWM_GET PWM_GET16
+#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 // 15-bit PWM+DSM ramp
+
+#define PWM_TOP ICR1 // holds the TOP value for variable-resolution PWM
+#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 90 // less time in delay() because more time spent in interrupts
+
+// regulated channel
+uint16_t ch1_dsm_lvl;
+uint8_t ch1_pwm, ch1_dsm;
+#define CH1_PIN PB3 // pin 16, Opamp reference
+#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3
+
+#define CH1_ENABLE_PIN PB0 // pin 19, Opamp power
+#define CH1_ENABLE_PORT PORTB // control port for PB0
+
+#define CH1_ENABLE_PIN2 PC0 // pin 15, boost PMIC enable
+#define CH1_ENABLE_PORT2 PORTC // control port for PC0
+
+// e-switch
+#define SWITCH_PIN PA7 // pin 20
+#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt
+#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
+#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
+
+// this light has 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
+#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
+
+inline void hwdef_setup() {
+ // enable output ports
+ // boost PMIC on/off
+ DDRC = (1 << CH1_ENABLE_PIN2);
+ // Opamp level and Opamp on/off
+ DDRB = (1 << CH1_PIN)
+ | (1 << CH1_ENABLE_PIN);
+ // aux R/G/B
+ DDRA = (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ ;
+
+ // 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
+ // 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) | (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
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-noctigon-k1-sbt90.h b/hwdef-noctigon-k1-sbt90.h
new file mode 100644
index 0000000..c89c3fc
--- /dev/null
+++ b/hwdef-noctigon-k1-sbt90.h
@@ -0,0 +1,177 @@
+// Noctigon K1-SBT90.2 driver layout (attiny1634)
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * (mostly the same as KR4 driver)
+ *
+ * Pin / Name / Function
+ * 1 PA6 FET PWM (direct drive) (PWM1B)
+ * 2 PA5 R: red aux LED (PWM0B)
+ * 3 PA4 G: green aux LED
+ * 4 PA3 B: blue aux LED
+ * 5 PA2 (none)
+ * 6 PA1 (none)
+ * 7 PA0 (none)
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 (none) PWM0A
+ * 16 PB3 main LED PWM (linear) (PWM1A)
+ * 17 PB2 MISO / e-switch (PCINT10)
+ * 18 PB1 MOSI / battery voltage (ADC6)
+ * 19 PB0 Opamp power
+ * 20 PA7 (none)
+ * ADC12 thermal sensor
+ *
+ * Main LED power uses one pin to turn the Opamp on/off,
+ * and one pin to control Opamp power level.
+ * Main brightness control uses the power level pin, with dynamic PWM.
+ * The on/off pin is only used to turn the main LED on and off,
+ * not to change brightness.
+ * Also has a direct-drive FET for turbo.
+ */
+
+#define ATTINY 1634
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-noctigon-kr4.c
+
+// allow using aux LEDs as extra channel modes
+#include "chan-rgbaux.h"
+
+// channel modes:
+// * 0. linear + DD FET stacked
+// * 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
+// no args
+//#define USE_CHANNEL_MODE_ARGS
+//#define CHANNEL_MODE_ARGS 0,0,0,0,0,0,0,0
+
+
+#define PWM_CHANNELS 2 // old, remove this
+
+#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255
+#define PWM_GET PWM_GET8
+#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255)
+#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255
+#define PWM1_DATATYPE uint8_t // linear ramp
+#define PWM2_DATATYPE uint8_t // DD FET ramp
+
+// PWM parameters of both channels are tied together because they share a counter
+#define PWM_TOP ICR1 // holds the TOP value for variable-resolution PWM
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+#define PWM_CNT TCNT1 // for dynamic PWM, reset phase
+
+// linear channel
+#define CH1_PIN PB3 // pin 16, Opamp reference
+#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3
+#define CH1_ENABLE_PIN PB0 // pin 19, Opamp power
+#define CH1_ENABLE_PORT PORTB // control port for PB0
+
+// DD FET channel
+#define CH2_PIN PA6 // pin 1, DD FET PWM
+#define CH2_PWM OCR1B // OCR1B is the output compare register for PA6
+
+// e-switch
+#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 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 TEMP_CHANNEL 0b00001111
+
+// this light has 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
+#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
+
+inline void hwdef_setup() {
+ // enable output ports
+ // Opamp level and Opamp on/off
+ DDRB = (1 << CH1_PIN)
+ | (1 << CH1_ENABLE_PIN);
+ // DD FET PWM, aux R/G/B
+ DDRA = (1 << CH2_PIN)
+ | (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ ;
+
+ // 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]: 1,0,1,0: PWM, Phase Correct, 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) | (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) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5)
+ ;
+
+ // set PWM resolution
+ PWM_TOP = PWM_TOP_INIT;
+
+ // set up e-switch
+ SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch
+ SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-noctigon-k1.c b/hwdef-noctigon-k1.c
new file mode 100644
index 0000000..5d61860
--- /dev/null
+++ b/hwdef-noctigon-k1.c
@@ -0,0 +1,65 @@
+// Noctigon K1 PWM helper functions
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "chan-rgbaux.c"
+
+void set_level_zero();
+
+void set_level_main(uint8_t level);
+bool gradual_tick_main(uint8_t gt);
+
+
+Channel channels[] = {
+ { // channel 1 only
+ .set_level = set_level_main,
+ .gradual_tick = gradual_tick_main
+ },
+ RGB_AUX_CHANNELS
+};
+
+
+void set_level_zero() {
+ CH1_PWM = 0;
+ //CH2_PWM = 0;
+ //PWM_CNT = 0; // reset phase
+ CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp
+}
+
+// single LED with 1 power channels, linear
+void set_level_main(uint8_t level) {
+ CH1_ENABLE_PORT |= (1 << CH1_ENABLE_PIN); // enable opamp
+
+ PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level);
+ //PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level);
+ // pulse frequency modulation, a.k.a. dynamic PWM
+ //uint16_t top = PWM_GET16(pwm_tops, level);
+
+ CH1_PWM = ch1_pwm;
+ //CH2_PWM = ch2_pwm;
+ // wait to sync the counter and avoid flashes
+ //while(actual_level && (PWM_CNT > (top - 32))) {}
+ //PWM_TOP = top;
+ // force reset phase when turning on from zero
+ // (because otherwise the initial response is inconsistent)
+ //if (! actual_level) PWM_CNT = 0;
+}
+
+bool gradual_tick_main(uint8_t gt) {
+ PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt);
+ //PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt);
+
+ GRADUAL_ADJUST_SIMPLE (pwm1, CH1_PWM);
+ //GRADUAL_ADJUST_STACKED(pwm1, CH1_PWM, PWM_TOP_INIT);
+ //GRADUAL_ADJUST_SIMPLE (pwm2, CH2_PWM);
+
+ if ( (pwm1 == CH1_PWM)
+ // && (pwm2 == CH2_PWM)
+ ) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
diff --git a/hwdef-noctigon-k1.h b/hwdef-noctigon-k1.h
new file mode 100644
index 0000000..6467567
--- /dev/null
+++ b/hwdef-noctigon-k1.h
@@ -0,0 +1,170 @@
+// Noctigon K1 driver layout (attiny1634)
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * (originally known as Emisar D1S V2)
+ *
+ * Pin / Name / Function
+ * 1 PA6 (none) (PWM1B) (reserved for DD drivers)
+ * 2 PA5 R: red aux LED (PWM0B)
+ * 3 PA4 G: green aux LED
+ * 4 PA3 B: blue aux LED
+ * 5 PA2 (none) (reserved for L: button LED (on some models))
+ * 6 PA1 (none)
+ * 7 PA0 (none)
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 (none) PWM0A
+ * 16 PB3 main LED PWM (PWM1A)
+ * 17 PB2 MISO
+ * 18 PB1 MOSI / battery voltage (ADC6)
+ * 19 PB0 Opamp power
+ * 20 PA7 e-switch (PCINT7)
+ * ADC12 thermal sensor
+ *
+ * Main LED power uses one pin to turn the Opamp on/off,
+ * and one pin to control Opamp power level.
+ * All brightness control uses the power level pin, with 4 kHz 10-bit PWM.
+ * The on/off pin is only used to turn the main LED on and off,
+ * not to change brightness.
+ */
+
+#define ATTINY 1634
+#include <avr/io.h>
+
+#ifndef HWDEF_C_FILE
+#define HWDEF_C_FILE hwdef-noctigon-k1.c
+#endif
+
+// allow using aux LEDs as extra channel modes
+#include "chan-rgbaux.h"
+
+// channel modes:
+// * 0. main LED
+// * 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
+// no args
+//#define USE_CHANNEL_MODE_ARGS
+//#define CHANNEL_MODE_ARGS 0,0,0,0,0,0,0,0
+
+
+#define PWM_CHANNELS 1 // old, remove this
+
+#define PWM_BITS 10 // 0 to 1023 at 4 kHz, not 0 to 255 at 16 kHz
+#define PWM_GET PWM_GET16
+#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255)
+#define PWM_DATATYPE2 uint32_t // only needs 32-bit if ramp values go over 255
+#define PWM1_DATATYPE uint16_t // linear ramp
+
+#define PWM_TOP ICR1 // holds the TOP value for variable-resolution PWM
+#define PWM_TOP_INIT 1023 // highest value used in top half of ramp
+#define PWM_CNT TCNT1 // for dynamic PWM, reset phase
+
+// linear channel
+#define CH1_PIN PB3 // pin 16, Opamp reference
+#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3
+#define CH1_ENABLE_PIN PB0 // pin 19, Opamp power
+#define CH1_ENABLE_PORT PORTB // control port for PB0
+
+// e-switch
+#define SWITCH_PIN PA7 // pin 20
+#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt
+#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
+#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
+
+// this light has 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
+#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
+
+
+inline void hwdef_setup() {
+ // enable output ports
+ // Opamp level and Opamp on/off
+ DDRB = (1 << CH1_PIN)
+ | (1 << CH1_ENABLE_PIN);
+ // aux R/G/B
+ DDRA = (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ ;
+
+ // 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)
+ // 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]: 0,0: PWM OC1B disabled (DS table 12-4)
+ TCCR1A = (1<<WGM11) | (1<<WGM10) // 10-bit (TOP=0x03FF) (DS table 12-5)
+ | (1<<COM1A1) | (0<<COM1A0) // PWM 1A in normal direction (DS table 12-4)
+ | (0<<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)
+ | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5)
+ ;
+
+ // set PWM resolution
+ //PWM_TOP = PWM_TOP_INIT;
+
+ // set up e-switch
+ SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch
+ SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-Noctigon_KR4-tintramp.h b/hwdef-noctigon-kr4-2ch.h
index 3bd57fe..a450693 100644
--- a/hwdef-Noctigon_KR4-tintramp.h
+++ b/hwdef-noctigon-kr4-2ch.h
@@ -1,8 +1,10 @@
-#ifndef HWDEF_KR4_TINTRAMP_H
-#define HWDEF_KR4_TINTRAMP_H
+// Noctigon KR4 w/ tint ramping
+// Copyright (C) 2021-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
-/* Noctigon KR4 w/ tint ramping
- * (same driver as D4Sv2-tintramp, but with the switch on a different pin)
+/*
+ * (same driver as emisar-2ch, but with the switch on a different pin)
*
* Pin / Name / Function
* 1 PA6 2nd LED PWM (linear) (PWM1B)
@@ -40,7 +42,6 @@
#define SWITCH_PUE PUEB // pullup group B
#define PCINT_vect PCINT1_vect // ISR for PCINT[11:8]
-// the rest of the config is the same as D4Sv2-tintramp
-#include "hwdef-Emisar_D4Sv2-tintramp.h"
+// the rest of the config is the same as the generic Emisar 2ch build
+#include "hwdef-emisar-2ch.h"
-#endif
diff --git a/hwdef-noctigon-kr4-boost.h b/hwdef-noctigon-kr4-boost.h
new file mode 100644
index 0000000..07c4f4b
--- /dev/null
+++ b/hwdef-noctigon-kr4-boost.h
@@ -0,0 +1,57 @@
+// Noctigon KR4 boost driver layout (attiny1634)
+// Copyright (C) 2020-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * (based on Noctigon DM11-boost and KR4)
+ * (is basically the same except the switch is on a different pin)
+ *
+ * Pin / Name / Function
+ * 1 PA6 (none) (PWM1B) (reserved for DD drivers)
+ * 2 PA5 R: red aux LED (PWM0B)
+ * 3 PA4 G: green aux LED
+ * 4 PA3 B: blue aux LED
+ * 5 PA2 L: button LED
+ * 6 PA1 (none)
+ * 7 PA0 (none)
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 boost PMIC enable (PWM0A not used)
+ * 16 PB3 main LED PWM (PWM1A)
+ * 17 PB2 MISO / e-switch (PCINT10)
+ * 18 PB1 MOSI / battery voltage (ADC6)
+ * 19 PB0 Opamp power
+ * 20 PA7 (none) (PCINT7)
+ * ADC12 thermal sensor
+ *
+ * Main LED power uses one pin to turn the Opamp on/off,
+ * and one pin to control Opamp power level.
+ * Linear brightness control uses the power level pin, with dynamic PWM.
+ * The on/off pin is only used to turn the main LED on and off,
+ * not to change brightness.
+ */
+
+#include "hwdef-noctigon-dm11-boost.h"
+
+// e-switch is on a different pin
+#undef SWITCH_PIN
+#undef SWITCH_PCINT
+#undef SWITCH_PCIE
+#undef SWITCH_PCMSK
+#undef SWITCH_PORT
+#undef SWITCH_PUE
+#undef PCINT_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]
+
diff --git a/hwdef-noctigon-kr4-nofet.c b/hwdef-noctigon-kr4-nofet.c
new file mode 100644
index 0000000..0492def
--- /dev/null
+++ b/hwdef-noctigon-kr4-nofet.c
@@ -0,0 +1,60 @@
+// Noctigon KR4 (no DD FET) PWM helper functions
+// Copyright (C) 2020-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "chan-rgbaux.c"
+
+void set_level_zero();
+
+void set_level_main(uint8_t level);
+bool gradual_tick_main(uint8_t gt);
+
+
+Channel channels[] = {
+ { // channel 1 only
+ .set_level = set_level_main,
+ .gradual_tick = gradual_tick_main
+ },
+ RGB_AUX_CHANNELS
+};
+
+
+void set_level_zero() {
+ CH1_PWM = 0;
+ CH2_PWM = 0;
+ PWM_CNT = 0; // reset phase
+ CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp
+}
+
+// single set of LEDs with linear power channel
+void set_level_main(uint8_t level) {
+ CH1_ENABLE_PORT |= (1 << CH1_ENABLE_PIN); // enable opamp
+
+ PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level);
+ // pulse frequency modulation, a.k.a. dynamic PWM
+ uint16_t top = PWM_GET16(pwm_tops, level);
+
+ CH1_PWM = ch1_pwm;
+ CH2_PWM = 0;
+ // wait to sync the counter and avoid flashes
+ while(actual_level && (PWM_CNT > (top - 32))) {}
+ PWM_TOP = top;
+ // force reset phase when turning on from zero
+ // (because otherwise the initial response is inconsistent)
+ if (! actual_level) PWM_CNT = 0;
+}
+
+bool gradual_tick_main(uint8_t gt) {
+ PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt);
+
+ GRADUAL_ADJUST_SIMPLE(pwm1, CH1_PWM);
+
+ if ( (pwm1 == CH1_PWM)
+ ) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
diff --git a/hwdef-noctigon-kr4.c b/hwdef-noctigon-kr4.c
new file mode 100644
index 0000000..884151d
--- /dev/null
+++ b/hwdef-noctigon-kr4.c
@@ -0,0 +1,63 @@
+// Noctigon KR4 PWM helper functions
+// Copyright (C) 2020-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#include "chan-rgbaux.c"
+
+void set_level_zero();
+
+void set_level_main(uint8_t level);
+bool gradual_tick_main(uint8_t gt);
+
+
+Channel channels[] = {
+ { // channel 1 only
+ .set_level = set_level_main,
+ .gradual_tick = gradual_tick_main
+ },
+ RGB_AUX_CHANNELS
+};
+
+
+void set_level_zero() {
+ CH1_PWM = 0;
+ CH2_PWM = 0;
+ PWM_CNT = 0; // reset phase
+ CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp
+}
+
+// single set of LEDs with 2 stacked power channels, linear + DD FET
+void set_level_main(uint8_t level) {
+ CH1_ENABLE_PORT |= (1 << CH1_ENABLE_PIN); // enable opamp
+
+ PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level);
+ PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level);
+ // pulse frequency modulation, a.k.a. dynamic PWM
+ uint16_t top = PWM_GET16(pwm_tops, level);
+
+ CH1_PWM = ch1_pwm;
+ CH2_PWM = ch2_pwm;
+ // wait to sync the counter and avoid flashes
+ while(actual_level && (PWM_CNT > (top - 32))) {}
+ PWM_TOP = top;
+ // force reset phase when turning on from zero
+ // (because otherwise the initial response is inconsistent)
+ if (! actual_level) PWM_CNT = 0;
+}
+
+bool gradual_tick_main(uint8_t gt) {
+ PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt);
+ PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt);
+
+ GRADUAL_ADJUST_STACKED(pwm1, CH1_PWM, PWM_TOP_INIT);
+ GRADUAL_ADJUST_SIMPLE (pwm2, CH2_PWM);
+
+ if ( (pwm1 == CH1_PWM)
+ && (pwm2 == CH2_PWM)
+ ) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
diff --git a/hwdef-noctigon-kr4.h b/hwdef-noctigon-kr4.h
new file mode 100644
index 0000000..5570fb7
--- /dev/null
+++ b/hwdef-noctigon-kr4.h
@@ -0,0 +1,194 @@
+// Noctigon KR4 / D4V2.5 driver layout (attiny1634)
+// Copyright (C) 2020-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * Pin / Name / Function
+ * 1 PA6 FET PWM (direct drive) (PWM1B) (on some models)
+ * 2 PA5 R: red aux LED (PWM0B)
+ * 3 PA4 G: green aux LED
+ * 4 PA3 B: blue aux LED
+ * 5 PA2 L: button LED (on some models)
+ * 6 PA1 (none)
+ * 7 PA0 (none)
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 (none) PWM0A
+ * 16 PB3 main LED PWM (linear) (PWM1A)
+ * 17 PB2 MISO / e-switch (PCINT10)
+ * 18 PB1 MOSI / battery voltage (ADC6)
+ * 19 PB0 Opamp power
+ * 20 PA7 (none)
+ * ADC12 thermal sensor
+ *
+ * Main LED power uses one pin to turn the Opamp on/off,
+ * and one pin to control Opamp power level.
+ * Linear brightness control uses the power level pin, with dynamic PWM.
+ * The on/off pin is only used to turn the main LED on and off,
+ * not to change brightness.
+ * Some models also have a direct-drive FET for turbo.
+ */
+
+#define ATTINY 1634
+#include <avr/io.h>
+
+#ifndef HWDEF_C_FILE
+#define HWDEF_C_FILE hwdef-noctigon-kr4.c
+#endif
+
+// allow using aux LEDs as extra channel modes
+#include "chan-rgbaux.h"
+
+// channel modes:
+// * 0. linear + DD FET stacked
+// * 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
+// no args
+//#define USE_CHANNEL_MODE_ARGS
+//#define CHANNEL_MODE_ARGS 0,0,0,0,0,0,0,0
+
+
+#define PWM_CHANNELS 2 // old, remove this
+
+#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255
+#define PWM_GET PWM_GET8
+#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255)
+#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255
+#define PWM1_DATATYPE uint8_t // linear ramp
+#define PWM2_DATATYPE uint8_t // DD FET ramp
+
+// PWM parameters of both channels are tied together because they share a counter
+#define PWM_TOP ICR1 // holds the TOP value for variable-resolution PWM
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+#define PWM_CNT TCNT1 // for dynamic PWM, reset phase
+
+// linear channel
+#define CH1_PIN PB3 // pin 16, Opamp reference
+#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3
+#define CH1_ENABLE_PIN PB0 // pin 19, Opamp power
+#define CH1_ENABLE_PORT PORTB // control port for PB0
+
+// DD FET channel
+#define CH2_PIN PA6 // pin 1, DD FET PWM
+#define CH2_PWM OCR1B // OCR1B is the output compare register for PA6
+
+// e-switch
+#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]
+
+// 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
+
+// this light has 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
+#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
+
+#define BUTTON_LED_PIN PA2 // pin 5
+#define BUTTON_LED_PORT PORTA // for all "PA" pins
+#define BUTTON_LED_DDR DDRA // for all "PA" pins
+#define BUTTON_LED_PUE PUEA // for all "PA" pins
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+// some variants also have an independent LED in the button
+#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
+
+
+inline void hwdef_setup() {
+ // enable output ports
+ // Opamp level and Opamp on/off
+ DDRB = (1 << CH1_PIN)
+ | (1 << CH1_ENABLE_PIN);
+ // DD FET PWM, aux R/G/B, button LED
+ DDRA = (1 << CH2_PIN)
+ | (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ | (1 << BUTTON_LED_PIN)
+ ;
+
+ // 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]: 1,0,1,0: PWM, Phase Correct, 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) | (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) | (0<<WGM12) // phase-correct adjustable PWM (DS table 12-5)
+ ;
+
+ // set PWM resolution
+ PWM_TOP = PWM_TOP_INIT;
+
+ // set up e-switch
+ SWITCH_PUE = (1 << SWITCH_PIN); // pull-up for e-switch
+ SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-noctigon-m44.c b/hwdef-noctigon-m44.c
new file mode 100644
index 0000000..395a7a2
--- /dev/null
+++ b/hwdef-noctigon-m44.c
@@ -0,0 +1,262 @@
+// hwdef for Noctigon M44 2-channel light
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#include "chan-rgbaux.c"
+
+
+void set_level_zero();
+
+void set_level_ch1(uint8_t level);
+void set_level_ch2(uint8_t level);
+void set_level_both(uint8_t level);
+void set_level_blend(uint8_t level);
+void set_level_auto(uint8_t level);
+
+bool gradual_tick_ch1(uint8_t gt);
+bool gradual_tick_ch2(uint8_t gt);
+bool gradual_tick_both(uint8_t gt);
+bool gradual_tick_blend(uint8_t gt);
+bool gradual_tick_auto(uint8_t gt);
+
+
+Channel channels[] = {
+ { // channel 1 only
+ .set_level = set_level_ch1,
+ .gradual_tick = gradual_tick_ch1,
+ .has_args = 0
+ },
+ { // channel 2 only
+ .set_level = set_level_ch2,
+ .gradual_tick = gradual_tick_ch2,
+ .has_args = 0
+ },
+ { // both channels, tied together (max "200%" power)
+ .set_level = set_level_both,
+ .gradual_tick = gradual_tick_both,
+ .has_args = 0
+ },
+ { // both channels, manual blend (max "100%" power)
+ .set_level = set_level_blend,
+ .gradual_tick = gradual_tick_blend,
+ .has_args = 1
+ },
+ { // both channels, auto blend
+ .set_level = set_level_auto,
+ .gradual_tick = gradual_tick_auto,
+ .has_args = 1
+ },
+ RGB_AUX_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;
+ ch2_dsm_lvl = 0;
+ CH1_PWM = 0;
+ CH2_PWM = 0;
+ PWM_CNT = 0;
+ CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp
+ CH2_ENABLE_PORT &= ~(1 << CH2_ENABLE_PIN); // disable opamp
+}
+
+
+// wrap setting the dsm vars, to get a faster response
+// (just setting *_dsm_lvl doesn't work well for strobes)
+// set new values for both channels,
+// handling any possible combination
+// and any before/after state
+void set_hw_levels(PWM_DATATYPE ch1, PWM_DATATYPE ch2,
+ bool ch1_on, bool ch2_on) {
+
+ bool was_on = (CH1_PWM>0) || (CH2_PWM>0)
+ || ( CH1_ENABLE_PORT & (1 << CH1_ENABLE_PIN) )
+ || ( CH2_ENABLE_PORT & (1 << CH2_ENABLE_PIN) );
+ //bool now_on = (ch1>0) || (ch2>0) || ch1_on || ch2_on;
+
+ #if 0 // not needed any more, after switching to PWM+DSM
+ // phase-correct PWM at zero (for flicker-free moon),
+ // fast PWM otherwise
+ if (ch1 || ch2)
+ 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)
+ ;
+ else
+ 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)
+ ;
+ #endif
+
+ if (ch1 || ch1_on)
+ CH1_ENABLE_PORT |= (1 << CH1_ENABLE_PIN); // enable opamp
+ else
+ CH1_ENABLE_PORT &= ~(1 << CH1_ENABLE_PIN); // disable opamp
+
+ if (ch2 || ch2_on)
+ CH2_ENABLE_PORT |= (1 << CH2_ENABLE_PIN); // enable opamp
+ else
+ CH2_ENABLE_PORT &= ~(1 << CH2_ENABLE_PIN); // disable opamp
+
+ // set delta-sigma soft levels
+ ch1_dsm_lvl = ch1;
+ ch2_dsm_lvl = ch2;
+
+ // set hardware PWM levels and init dsm loop
+ CH1_PWM = ch1_pwm = ch1 >> 7;
+ CH2_PWM = ch2_pwm = ch2 >> 7;
+
+ #if 0 // not needed any more, after switching to PWM+DSM
+ // manual phase sync when changing level while already on
+ if (was_on && now_on) while(PWM_CNT > (top - 32)) {}
+
+ PWM_TOP = top;
+ #endif
+
+ // enable timer overflow interrupt so DSM can work
+ DSM_INTCTRL |= DSM_OVF_bm;
+
+ // reset phase when turning on
+ //if ((! was_on) | (! now_on)) PWM_CNT = 0;
+ if (! was_on) PWM_CNT = 0;
+
+}
+
+// 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;
+ CH2_PWM = ch2_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;
+
+ // repeat for other channels
+
+ ch2_dsm += (ch2_dsm_lvl & 0x007f);
+ ch2_pwm = (ch2_dsm_lvl >> 7) + (ch2_dsm > 0x7f);
+ ch2_dsm &= 0x7f;
+}
+
+
+void set_level_ch1(uint8_t level) {
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, level);
+ set_hw_levels(pwm, 0,
+ 1, 0);
+}
+
+void set_level_ch2(uint8_t level) {
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, level);
+ set_hw_levels(0, pwm,
+ 0, 1);
+}
+
+void set_level_both(uint8_t level) {
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, level);
+ set_hw_levels(pwm, pwm,
+ 1, 1);
+}
+
+void blend_helper(PWM_DATATYPE *warm, PWM_DATATYPE *cool, uint8_t level) {
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
+ uint8_t blend;
+ if (channel_mode == CM_AUTO) {
+ blend = 255 * (uint16_t)level / RAMP_SIZE;
+ if (cfg.channel_mode_args[channel_mode] & 0b01000000)
+ blend = 255 - blend;
+ } else {
+ blend = cfg.channel_mode_args[channel_mode];
+ }
+
+ calc_2ch_blend(warm, cool, brightness, DSM_TOP, blend);
+}
+
+void set_level_blend(uint8_t level) {
+ PWM_DATATYPE warm, cool;
+ uint8_t blend = cfg.channel_mode_args[channel_mode];
+ blend_helper(&warm, &cool, level);
+ // don't turn off either emitter entirely while using middle blends
+ set_hw_levels(warm, cool,
+ blend < 255, blend > 0);
+}
+
+void set_level_auto(uint8_t level) {
+ PWM_DATATYPE warm, cool;
+ blend_helper(&warm, &cool, level);
+ // don't turn off either emitter entirely
+ // (it blinks pretty bright when the regulator turns on mid-ramp)
+ set_hw_levels(warm, cool,
+ 1, 1);
+}
+
+///// "gradual tick" functions for smooth thermal regulation /////
+// (and other smooth adjustments)
+
+///// bump each channel toward a target value /////
+bool gradual_adjust(PWM_DATATYPE ch1, PWM_DATATYPE ch2) {
+ // 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);
+
+ steps = ch2_dsm_lvl >> shift;
+ for (uint8_t i=0; i<=steps; i++)
+ GRADUAL_ADJUST_SIMPLE(ch2, ch2_dsm_lvl);
+
+ if ((ch1 == ch1_dsm_lvl)
+ && (ch2 == ch2_dsm_lvl )) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
+bool gradual_tick_ch1(uint8_t gt) {
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(pwm, 0);
+}
+
+bool gradual_tick_ch2(uint8_t gt) {
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(0, pwm);
+}
+
+bool gradual_tick_both(uint8_t gt) {
+ PWM_DATATYPE pwm = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(pwm, pwm);
+}
+
+bool gradual_tick_blend(uint8_t gt) {
+ PWM_DATATYPE warm, cool;
+ blend_helper(&warm, &cool, gt);
+ return gradual_adjust(warm, cool);
+}
+
+bool gradual_tick_auto(uint8_t gt) {
+ PWM_DATATYPE warm, cool;
+ blend_helper(&warm, &cool, gt);
+ return gradual_adjust(warm, cool);
+}
+
+
diff --git a/hwdef-noctigon-m44.h b/hwdef-noctigon-m44.h
new file mode 100644
index 0000000..5658c9f
--- /dev/null
+++ b/hwdef-noctigon-m44.h
@@ -0,0 +1,206 @@
+// hwdef for Noctigon M44 2-channel light
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * Pin / Name / Function
+ * 1 PA6 ch2 LED PWM (boost) (PWM1B)
+ * 2 PA5 R: red aux LED (PWM0B)
+ * 3 PA4 G: green aux LED
+ * 4 PA3 B: blue aux LED
+ * 5 PA2 button LED
+ * 6 PA1 (none)
+ * 7 PA0 Opamp 2 enable (channel 2 LEDs)
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 (none)
+ * 16 PB3 ch1 LED PWM (linear) (PWM1A)
+ * 17 PB2 MISO
+ * 18 PB1 MOSI / battery voltage (ADC6)
+ * 19 PB0 Opamp 1 enable (channel 1 LEDs)
+ * 20 PA7 e-switch (PCINT7)
+ * ADC12 thermal sensor
+ */
+
+#define ATTINY 1634
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-noctigon-m44.c
+
+// allow using aux LEDs as extra channel modes
+#include "chan-rgbaux.h"
+
+// channel modes:
+// * 0. channel 1 only
+// * 1. channel 2 only
+// * 2. both channels, tied together, max "200%" power
+// * 3. both channels, manual blend, max "100%" power
+// * 4? both channels, manual blend, max 200% power
+// * 4. both channels, auto blend, reversible
+#define NUM_CHANNEL_MODES (5 + NUM_RGB_AUX_CHANNEL_MODES)
+enum channel_modes_e {
+ CM_CH1 = 0,
+ CM_CH2,
+ CM_BOTH,
+ CM_BLEND,
+ CM_AUTO,
+ RGB_AUX_ENUMS
+};
+
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b0000000000011111
+#define USE_CHANNEL_MODE_ARGS
+// _, _, _, 128=middle CCT, 0=warm-to-cool
+#define CHANNEL_MODE_ARGS 0,0,0,128,0,RGB_AUX_CM_ARGS
+
+// can use some of the common handlers
+#define USE_CALC_2CH_BLEND
+
+
+#define PWM_CHANNELS 1 // old, remove this
+
+#define PWM_BITS 16 // 0 to 32640 (0 to 255 PWM + 0 to 127 DSM) at constant kHz
+#define PWM_GET PWM_GET16
+#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 // 15-bit PWM+DSM ramp
+//#define PWM2_DATATYPE uint16_t // max "200% power" ramp table
+
+// PWM parameters of both channels are tied together because they share a counter
+// dynamic PWM
+#define PWM_TOP ICR1 // holds the TOP value for for variable-resolution PWM
+#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 90 // less time in delay() because more time spent in interrupts
+
+// 1st channel (8 LEDs)
+uint16_t ch1_dsm_lvl;
+uint8_t ch1_pwm, ch1_dsm;
+#define CH1_PIN PB3 // pin 16, Opamp reference
+#define CH1_PWM OCR1A // OCR1A is the output compare register for PB3
+#define CH1_ENABLE_PIN PB0 // pin 19, Opamp power
+#define CH1_ENABLE_PORT PORTB // control port for PB0
+
+// 2nd channel (8 LEDs)
+uint16_t ch2_dsm_lvl;
+uint8_t ch2_pwm, ch2_dsm;
+#define CH2_PIN PA6 // pin 1, 2nd LED Opamp reference
+#define CH2_PWM OCR1B // OCR1B is the output compare register for PA6
+#define CH2_ENABLE_PIN PA0 // pin 7, Opamp power
+#define CH2_ENABLE_PORT PORTA // control port for PA0
+
+
+// e-switch
+#define SWITCH_PIN PA7 // pin 20
+#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt
+#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
+#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
+
+// this light has 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
+#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
+
+#define BUTTON_LED_PIN PA2 // pin 5
+#define BUTTON_LED_PORT PORTA // for all "PA" pins
+#define BUTTON_LED_DDR DDRA // for all "PA" pins
+#define BUTTON_LED_PUE PUEA // for all "PA" pins
+
+
+inline void hwdef_setup() {
+ // enable output ports
+ //DDRC = (1 << CH3_PIN);
+ DDRB = (1 << CH1_PIN)
+ | (1 << CH1_ENABLE_PIN)
+ ;
+ DDRA = (1 << CH2_PIN)
+ | (1 << CH2_ENABLE_PIN)
+ | (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ | (1 << BUTTON_LED_PIN)
+ ;
+
+ // 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
+ // 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) | (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
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-sofirn-lt1s-pro.c b/hwdef-sofirn-lt1s-pro.c
new file mode 100644
index 0000000..90c2c07
--- /dev/null
+++ b/hwdef-sofirn-lt1s-pro.c
@@ -0,0 +1,237 @@
+// BLF LT1S Pro hwdef functions
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+
+void set_level_zero();
+
+void set_level_red(uint8_t level);
+void set_level_white_blend(uint8_t level);
+void set_level_auto_2ch_blend(uint8_t level);
+void set_level_auto_3ch_blend(uint8_t level);
+void set_level_red_white_blend(uint8_t level);
+
+bool gradual_tick_red(uint8_t gt);
+bool gradual_tick_white_blend(uint8_t gt);
+bool gradual_tick_auto_2ch_blend(uint8_t gt);
+bool gradual_tick_auto_3ch_blend(uint8_t gt);
+bool gradual_tick_red_white_blend(uint8_t gt);
+
+
+Channel channels[] = {
+ { // manual blend of warm and cool white
+ .set_level = set_level_white_blend,
+ .gradual_tick = gradual_tick_white_blend,
+ .has_args = 1
+ },
+ { // auto blend from warm white to cool white
+ .set_level = set_level_auto_2ch_blend,
+ .gradual_tick = gradual_tick_auto_2ch_blend,
+ .has_args = 0
+ },
+ { // auto blend from red to warm white to cool white
+ .set_level = set_level_auto_3ch_blend,
+ .gradual_tick = gradual_tick_auto_3ch_blend,
+ .has_args = 0
+ },
+ { // red only
+ .set_level = set_level_red,
+ .gradual_tick = gradual_tick_red,
+ .has_args = 0
+ },
+ { // manual white blend + adjustable red
+ .set_level = set_level_red_white_blend,
+ .gradual_tick = gradual_tick_red_white_blend,
+ .has_args = 1
+ }
+};
+
+
+// calculate a 3-channel "auto tint" blend
+// (like red -> warm white -> cool white)
+// results are placed in *a, *b, and *c vars
+// level : ramp level to convert into 3 channel levels
+// (assumes ramp table is "pwm1_levels")
+void calc_auto_3ch_blend(
+ PWM_DATATYPE *a,
+ PWM_DATATYPE *b,
+ PWM_DATATYPE *c,
+ uint8_t level) {
+
+ PWM_DATATYPE vpwm = PWM_GET(pwm1_levels, level);
+
+ // tint goes from 0 (red) to 127 (warm white) to 255 (cool white)
+ uint8_t mytint;
+ mytint = 255 * (uint16_t)level / RAMP_SIZE;
+
+ // red is high at 0, low at 255 (linear)
+ *a = (((PWM_DATATYPE2)(255 - mytint)
+ * (PWM_DATATYPE2)vpwm) + 127) / 255;
+ // warm white is low at 0 and 255, high at 127 (linear triangle)
+ *b = (((PWM_DATATYPE2)triangle_wave(mytint)
+ * (PWM_DATATYPE2)vpwm) + 127) / 255;
+ // cool white is low at 0, high at 255 (linear)
+ *c = (((PWM_DATATYPE2)mytint
+ * (PWM_DATATYPE2)vpwm) + 127) / 255;
+
+}
+
+
+void set_level_zero() {
+ WARM_PWM_LVL = 0;
+ COOL_PWM_LVL = 0;
+ RED_PWM_LVL = 0;
+ PWM_CNT = 0; // reset phase
+}
+
+// single set of LEDs with 1 power channel and dynamic PWM
+void set_level_red(uint8_t level) {
+ RED_PWM_LVL = PWM_GET(pwm1_levels, level);
+ // pulse frequency modulation, a.k.a. dynamic PWM
+ PWM_TOP = PWM_GET(pwm_tops, level);
+ // force reset phase when turning on from zero
+ // (because otherwise the initial response is inconsistent)
+ if (! actual_level) PWM_CNT = 0;
+}
+
+
+// warm + cool blend w/ dynamic PWM
+void set_level_white_blend(uint8_t level) {
+ PWM_DATATYPE warm_PWM, cool_PWM;
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
+ PWM_DATATYPE top = PWM_GET(pwm_tops, level);
+ uint8_t blend = cfg.channel_mode_args[channel_mode];
+
+ calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
+
+ WARM_PWM_LVL = warm_PWM;
+ COOL_PWM_LVL = cool_PWM;
+ PWM_TOP = top;
+ if (! actual_level) PWM_CNT = 0; // reset phase
+}
+
+
+// same as white blend, but tint is calculated from the ramp level
+void set_level_auto_2ch_blend(uint8_t level) {
+ PWM_DATATYPE warm_PWM, cool_PWM;
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
+ PWM_DATATYPE top = PWM_GET(pwm_tops, level);
+ uint8_t blend = 255 * (uint16_t)level / RAMP_SIZE;
+
+ calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
+
+ WARM_PWM_LVL = warm_PWM;
+ COOL_PWM_LVL = cool_PWM;
+ PWM_TOP = top;
+ if (! actual_level) PWM_CNT = 0; // reset phase
+}
+
+
+// "auto tint" channel mode with dynamic PWM
+void set_level_auto_3ch_blend(uint8_t level) {
+ PWM_DATATYPE a, b, c;
+ calc_auto_3ch_blend(&a, &b, &c, level);
+
+ // pulse frequency modulation, a.k.a. dynamic PWM
+ uint16_t top = PWM_GET(pwm_tops, level);
+
+ RED_PWM_LVL = a;
+ WARM_PWM_LVL = b;
+ COOL_PWM_LVL = c;
+ PWM_TOP = top;
+ if (! actual_level) PWM_CNT = 0;
+}
+
+
+// "white + red" channel mode
+void set_level_red_white_blend(uint8_t level) {
+ // set the warm+cool white LEDs first
+ channel_mode = CM_WHITE;
+ set_level_white_blend(level);
+ channel_mode = CM_WHITE_RED;
+
+ PWM_DATATYPE vpwm = PWM_GET(pwm1_levels, level);
+
+ // set the red LED as a ratio of the white output level
+ // 0 = no red
+ // 255 = red at 100% of white channel PWM
+ uint8_t ratio = cfg.channel_mode_args[channel_mode];
+
+ RED_PWM_LVL = (((PWM_DATATYPE2)ratio * (PWM_DATATYPE2)vpwm) + 127) / 255;
+ if (! actual_level) PWM_CNT = 0; // reset phase
+}
+
+
+///// "gradual tick" functions for smooth thermal regulation /////
+
+///// bump each channel toward a target value /////
+bool gradual_adjust(uint16_t red, uint16_t warm, uint16_t cool) {
+ GRADUAL_ADJUST_SIMPLE(red, RED_PWM_LVL );
+ GRADUAL_ADJUST_SIMPLE(warm, WARM_PWM_LVL);
+ GRADUAL_ADJUST_SIMPLE(cool, COOL_PWM_LVL);
+
+ // check for completion
+ if ((red == RED_PWM_LVL )
+ && (warm == WARM_PWM_LVL)
+ && (cool == COOL_PWM_LVL)) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
+bool gradual_tick_red(uint8_t gt) {
+ uint16_t red = PWM_GET(pwm1_levels, gt);
+ return gradual_adjust(red, 0, 0);
+}
+
+
+bool gradual_tick_white_blend(uint8_t gt) {
+ // figure out what exact PWM levels we're aiming for
+ PWM_DATATYPE warm_PWM, cool_PWM;
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt);
+ PWM_DATATYPE top = PWM_GET(pwm_tops, gt);
+ uint8_t blend = cfg.channel_mode_args[channel_mode];
+
+ calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
+
+ return gradual_adjust(0, warm_PWM, cool_PWM);
+}
+
+
+// same as white blend, but tint is calculated from the ramp level
+bool gradual_tick_auto_2ch_blend(uint8_t gt) {
+ // figure out what exact PWM levels we're aiming for
+ PWM_DATATYPE warm_PWM, cool_PWM;
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt);
+ PWM_DATATYPE top = PWM_GET(pwm_tops, gt);
+ uint8_t blend = 255 * (uint16_t)gt / RAMP_SIZE;
+
+ calc_2ch_blend(&warm_PWM, &cool_PWM, brightness, top, blend);
+
+ return gradual_adjust(0, warm_PWM, cool_PWM);
+}
+
+
+bool gradual_tick_auto_3ch_blend(uint8_t gt) {
+ // figure out what exact PWM levels we're aiming for
+ PWM_DATATYPE red, warm, cool;
+ calc_auto_3ch_blend(&red, &warm, &cool, gt);
+ return gradual_adjust(red, warm, cool);
+}
+
+
+bool gradual_tick_red_white_blend(uint8_t gt) {
+ // figure out what exact PWM levels we're aiming for
+ PWM_DATATYPE red, warm, cool;
+ PWM_DATATYPE brightness = PWM_GET(pwm1_levels, gt);
+ PWM_DATATYPE top = PWM_GET(pwm_tops, gt);
+ uint8_t blend = cfg.channel_mode_args[CM_WHITE];
+ uint8_t ratio = cfg.channel_mode_args[channel_mode];
+
+ red = (((PWM_DATATYPE2)ratio * (PWM_DATATYPE2)brightness) + 127) / 255;
+ calc_2ch_blend(&warm, &cool, brightness, top, blend);
+
+ return gradual_adjust(red, warm, cool);
+}
+
diff --git a/hwdef-sofirn-lt1s-pro.h b/hwdef-sofirn-lt1s-pro.h
new file mode 100644
index 0000000..ae6b3bf
--- /dev/null
+++ b/hwdef-sofirn-lt1s-pro.h
@@ -0,0 +1,151 @@
+// BLF LT1S Pro driver layout using the Attiny1616
+// Copyright (C) 2022-2023 (FIXME)
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * Driver pinout:
+ * eSwitch: PA5
+ * Aux LED: PB5
+ * WW PWM: PB0 (TCA0 WO0)
+ * CW PWM: PB1 (TCA0 WO1)
+ * Red PWM: PB2 (TCA0 WO2)
+ * Voltage: VCC
+ */
+
+#define ATTINY 1616
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-sofirn-lt1s-pro.c
+
+// channel modes:
+// * 0. warm/cool white blend
+// * 1. auto 2ch white blend (warm -> cool by ramp level)
+// * 2. auto 3ch blend (red -> warm -> cool by ramp level)
+// * 3. red only
+// * 4. red + white blend
+#define NUM_CHANNEL_MODES 5
+enum channel_modes_e {
+ CM_WHITE = 0,
+ CM_AUTO2,
+ CM_AUTO3,
+ CM_RED,
+ CM_WHITE_RED,
+};
+
+#define CHANNEL_MODES_ENABLED 0b00011111
+#define USE_CHANNEL_MODE_ARGS
+// 128=middle CCT, _, _, _, 255=100% red
+#define CHANNEL_MODE_ARGS 128,0,0,0,255
+
+// can use some of the common handlers
+#define USE_CALC_2CH_BLEND
+//#define USE_CALC_AUTO_3CH_BLEND
+
+
+#define PWM_CHANNELS 1 // old, remove this
+
+// only using 16-bit PWM on this light
+#define PWM_BITS 16
+
+#define PWM_GET PWM_GET16
+#define PWM_DATATYPE uint16_t
+#define PWM1_DATATYPE uint16_t
+#define PWM_DATATYPE2 uint32_t
+
+// dynamic PWM
+// PWM parameters of all channels are tied together because they share a counter
+#define PWM_TOP_INIT 511 // highest value used in the top half of the ramp
+#define PWM_TOP TCA0.SINGLE.PERBUF // holds the TOP value for for variable-resolution PWM
+#define PWM_CNT TCA0.SINGLE.CNT // for resetting phase after each TOP adjustment
+
+// warm LEDs
+//#define WARM_PWM_PIN PB0
+#define WARM_PWM_LVL TCA0.SINGLE.CMP0BUF // CMP1 is the output compare register for PB0
+
+// cold LEDs
+//#define COOL_PWM_PIN PB1
+#define COOL_PWM_LVL TCA0.SINGLE.CMP1BUF // CMP0 is the output compare register for PB1
+
+// red LEDs
+//#define RED_PWM_PIN PB2
+#define RED_PWM_LVL TCA0.SINGLE.CMP2BUF // CMP2 is the output compare register for PB2
+
+// lighted button
+#define AUXLED_PIN PIN5_bp
+#define AUXLED_PORT PORTB
+
+// the button lights up
+#define USE_INDICATOR_LED
+// the button is visible while main LEDs are on
+#define USE_INDICATOR_LED_WHILE_RAMPING
+
+// e-switch
+#define SWITCH_PIN PIN5_bp
+#define SWITCH_PORT VPORTA.IN
+#define SWITCH_ISC_REG PORTA.PIN2CTRL
+#define SWITCH_VECT PORTA_PORT_vect
+#define SWITCH_INTFLG VPORTA.INTFLAGS
+
+// average drop across diode on this hardware
+#ifndef VOLTAGE_FUDGE_FACTOR
+#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V
+#endif
+
+
+inline void hwdef_setup() {
+
+ // set up the system clock to run at 10 MHz instead of the default 3.33 MHz
+ _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB,
+ CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm );
+
+ //VPORTA.DIR = ...;
+ // Outputs
+ VPORTB.DIR = PIN0_bm // warm white
+ | PIN1_bm // cool white
+ | PIN2_bm // red
+ | PIN5_bm; // aux LED
+ //VPORTC.DIR = ...;
+
+ // enable pullups on the unused pins to reduce power
+ PORTA.PIN0CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN1CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN2CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN3CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN4CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN5CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // eSwitch
+ PORTA.PIN6CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN7CTRL = PORT_PULLUPEN_bm;
+
+ //PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // warm tint channel
+ //PORTB.PIN1CTRL = PORT_PULLUPEN_bm; // cold tint channel
+ //PORTB.PIN2CTRL = PORT_PULLUPEN_bm; // red LEDs
+ PORTB.PIN3CTRL = PORT_PULLUPEN_bm;
+ PORTB.PIN4CTRL = PORT_PULLUPEN_bm;
+ //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // Aux LED
+
+ PORTC.PIN0CTRL = PORT_PULLUPEN_bm;
+ PORTC.PIN1CTRL = PORT_PULLUPEN_bm;
+ PORTC.PIN2CTRL = PORT_PULLUPEN_bm;
+ PORTC.PIN3CTRL = PORT_PULLUPEN_bm;
+
+ // set up the PWM
+ // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf
+ // PB0 is TCA0:WO0, use TCA_SINGLE_CMP0EN_bm
+ // PB1 is TCA0:WO1, use TCA_SINGLE_CMP1EN_bm
+ // PB2 is TCA0:WO2, use TCA_SINGLE_CMP2EN_bm
+ // For Fast (Single Slope) PWM use TCA_SINGLE_WGMODE_SINGLESLOPE_gc
+ // For Phase Correct (Dual Slope) PWM use TCA_SINGLE_WGMODE_DSBOTTOM_gc
+ // TODO: add references to MCU documentation
+ TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm
+ | TCA_SINGLE_CMP1EN_bm
+ | TCA_SINGLE_CMP2EN_bm
+ | TCA_SINGLE_WGMODE_DSBOTTOM_gc;
+ PWM_TOP = PWM_TOP_INIT;
+ TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc
+ | TCA_SINGLE_ENABLE_bm;
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-sofirn-sp10-pro.c b/hwdef-sofirn-sp10-pro.c
new file mode 100644
index 0000000..42844a7
--- /dev/null
+++ b/hwdef-sofirn-sp10-pro.c
@@ -0,0 +1,63 @@
+// Sofirn SP10 Pro PWM helper functions
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+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
+ },
+};
+
+
+void set_level_zero() {
+ CH1_PWM = 0;
+ CH2_PWM = 0;
+ PWM_CNT = 0; // reset phase
+ BST_ENABLE_PORT &= ~(1 << BST_ENABLE_PIN); // boost off
+}
+
+// single set of LEDs with 2 stacked power channels
+void set_level_main(uint8_t level) {
+ PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level);
+ PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level);
+ // pulse frequency modulation, a.k.a. dynamic PWM
+ uint16_t top = PWM_GET16(pwm_tops, level);
+
+ BST_ENABLE_PORT |= (1 << BST_ENABLE_PIN); // boost on
+
+ CH1_PWM = ch1_pwm;
+ CH2_PWM = ch2_pwm;
+ PWM_TOP = top;
+
+ // force reset phase when turning on from zero
+ // (because otherwise the initial response is inconsistent)
+ if (! actual_level) PWM_CNT = 0;
+}
+
+bool gradual_tick_main(uint8_t gt) {
+ PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt);
+ PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt);
+
+ // ch1 sometimes makes huge leaps; don't make it gradual
+ // if either current or new level is in the leap zone, just leap
+ if ((CH1_PWM + pwm1) > 128) CH1_PWM = pwm1;
+ else GRADUAL_ADJUST_SIMPLE(pwm1, CH1_PWM);
+
+ GRADUAL_ADJUST_SIMPLE(pwm2, CH2_PWM);
+
+ if ( (pwm1 == CH1_PWM)
+ && (pwm2 == CH2_PWM)
+ ) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
diff --git a/hwdef-Sofirn_SP10-Pro.h b/hwdef-sofirn-sp10-pro.h
index d7c2081..5cef6a7 100644
--- a/hwdef-Sofirn_SP10-Pro.h
+++ b/hwdef-sofirn-sp10-pro.h
@@ -1,67 +1,75 @@
-#ifndef HWDEF_SOFIRN_SP10_H
-#define HWDEF_SOFIRN_SP10_H
+// Sofirn SP10 Pro pinout
+// Copyright (C) 2022-2023 (original author TBD), Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * ATTINY1616 Mapping:
+ * PB5 : PWM small channel (TCA0 - WO2 Alternate MUX)
+ * PB3 : eSwitch
+ * PB0 : PWM big channel (TCA0 - WO0)
+ * PB4 : Voltage divider (ADC0 - AIN9)
+ * PA1 : Boost Enable
+ */
-/* Sofirn SP10 Pro pinout
+#define ATTINY 1616
+#include <avr/io.h>
-ATTINY1616 Mapping:
-PB5 : PWM small channel (TCA0 - WO2 Alternate MUX)
-PB3 : eSwitch
-PB0 : PWM big channel (TCA0 - WO0)
-PB4 : Voltage divider (ADC0 - AIN9)
-PA1 : Boost Enable
+#define HWDEF_C_FILE hwdef-sofirn-sp10-pro.c
-*/
+// channel modes:
+// * 0. low+high PWM stacked
+#define NUM_CHANNEL_MODES 1
+enum CHANNEL_MODES {
+ CM_MAIN = 0,
+};
+#define DEFAULT_CHANNEL_MODE CM_MAIN
-#define LAYOUT_DEFINED
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b00000001
-#ifdef ATTINY
-#undef ATTINY
-#endif
-#define ATTINY 1616
-#include <avr/io.h>
-#ifndef SWITCH_PIN
-#define SWITCH_PIN 3
-#define SWITCH_PORT VPORTB.IN
-#define SWITCH_ISC_REG PORTB.PIN3CTRL
-#define SWITCH_VECT PORTB_PORT_vect
-#define SWITCH_INTFLG VPORTB.INTFLAGS
-#endif
+#define PWM_CHANNELS 2 // old, remove this
-#define PWM_CHANNELS 2
-#define PWM_BITS 16 // data type needs 16 bits, not 8
-#define PWM_TOP 255 // highest value used in top half of ramp
-#define USE_DYN_PWM // dynamic frequency and speed
+#define PWM_BITS 16 // data type needs 16 bits, not 8
+#define PWM_GET PWM_GET16
+#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255)
+#define PWM_DATATYPE2 uint32_t // only needs 32-bit if ramp values go over 255
+#define PWM1_DATATYPE uint16_t // low PWM ramp
+#define PWM2_DATATYPE uint16_t // high PWM ramp
+
+// PWM parameters of both channels are tied together because they share a counter
+#define PWM_TOP TCA0.SINGLE.PERBUF // holds the TOP value for for variable-resolution PWM
+#define PWM_CNT TCA0.SINGLE.CNT // for resetting phase after each TOP adjustment
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp (unused?)
// Small channel
-#ifndef PWM1_PIN
-#define PWM1_PIN PB5
-#define PWM1_LVL TCA0.SINGLE.CMP2BUF // PB5 is Alternate MUX for TCA Compare 2
-#endif
+#define CH1_PIN PB5
+#define CH1_PWM TCA0.SINGLE.CMP2BUF // PB5 is Alternate MUX for TCA Compare 2
// Big channel
-#ifndef PWM2_PIN
-#define PWM2_PIN PB0
-#define PWM2_LVL TCA0.SINGLE.CMP0BUF // PB0 is TCA Compare 0
-#endif
-
-// PWM parameters of both channels are tied together because they share a counter
-#define PWM1_TOP TCA0.SINGLE.PERBUF // holds the TOP value for for variable-resolution PWM
-// not necessary when double-buffered "BUF" registers are used
-#define PWM1_CNT TCA0.SINGLE.CNT // for resetting phase after each TOP adjustment
-#define PWM1_PHASE_RESET_OFF // force reset while shutting off
-#define PWM1_PHASE_RESET_ON // force reset while turning on
-//#define PWM1_PHASE_SYNC // manual sync while changing level
-
-#define LED_ENABLE_PIN PIN1_bp
-#define LED_ENABLE_PORT PORTA_OUT
-//#define LED_OFF_DELAY 4 // only needed when PWM1_PHASE_RESET_OFF not used
-
-#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened
-#define DUAL_VOLTAGE_FLOOR 21 // for AA/14500 boost drivers, don't indicate low voltage if below this level
+#define CH2_PIN PB0
+#define CH2_PWM TCA0.SINGLE.CMP0BUF // PB0 is TCA Compare 0
+
+// boost enable
+#define BST_ENABLE_PIN PIN1_bp
+#define BST_ENABLE_PORT PORTA_OUT
+
+// e-switch
+#define SWITCH_PIN 3
+#define SWITCH_PORT VPORTB.IN
+#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 ADMUX_VOLTAGE_DIVIDER ADC_MUXPOS_AIN9_gc // which ADC channel to read
+#define ADMUX_VOLTAGE_DIVIDER ADC_MUXPOS_AIN9_gc // which ADC channel to read
// Raw ADC readings at 4.4V and 2.2V
// calibrate the voltage readout here
@@ -77,15 +85,15 @@ PA1 : Boost Enable
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
inline void hwdef_setup() {
// set up the system clock to run at 10 MHz instead of the default 3.33 MHz
- _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm );
+ _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB,
+ CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm );
VPORTA.DIR = PIN1_bm; // Boost enable pin
- VPORTB.DIR = PIN0_bm | PIN5_bm; // PWM pins as output
+ VPORTB.DIR = PIN0_bm // big PWM channel
+ | PIN5_bm; // small PWM channel
//VPORTC.DIR = ...;
// enable pullups on the input pins to reduce power
@@ -101,7 +109,8 @@ inline void hwdef_setup() {
//PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // Big PWM channel
PORTB.PIN1CTRL = PORT_PULLUPEN_bm;
PORTB.PIN2CTRL = PORT_PULLUPEN_bm;
- PORTB.PIN3CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // Switch
+ PORTB.PIN3CTRL = PORT_PULLUPEN_bm
+ | PORT_ISC_BOTHEDGES_gc; // e-switch
//PORTB.PIN4CTRL = PORT_PULLUPEN_bm; // Voltage divider
//PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // Small PWM channel
@@ -119,9 +128,12 @@ inline void hwdef_setup() {
// For Phase Correct (Dual Slope) PWM use TCA_SINGLE_WGMODE_DSBOTTOM_gc
// See the manual for other pins, clocks, configs, portmux, etc
PORTMUX.CTRLC = PORTMUX_TCA02_ALTERNATE_gc; // Use alternate pin for TCA0:WO2
- TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm | TCA_SINGLE_CMP2EN_bm | TCA_SINGLE_WGMODE_DSBOTTOM_gc;
- PWM1_TOP = PWM_TOP;
- TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc | TCA_SINGLE_ENABLE_bm;
+ TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm
+ | TCA_SINGLE_CMP2EN_bm
+ | TCA_SINGLE_WGMODE_DSBOTTOM_gc;
+ PWM_TOP = PWM_TOP_INIT;
+ TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc
+ | TCA_SINGLE_ENABLE_bm;
}
@@ -131,7 +143,7 @@ inline void hwdef_setup() {
// command: avr-objdump -d -S -j .fuse anduril.elf
FUSES = {
.WDTCFG = FUSE_WDTCFG_DEFAULT, // Watchdog Configuration
- .BODCFG = FUSE_ACTIVE0_bm, // BOD Configuration
+ .BODCFG = FUSE_ACTIVE0, // BOD Configuration
.OSCCFG = FUSE_OSCCFG_DEFAULT, // Oscillator Configuration
.TCD0CFG = FUSE_TCD0CFG_DEFAULT, // TCD0 Configuration
.SYSCFG0 = FUSE_SYSCFG0_DEFAULT, // System Configuration 0
@@ -141,4 +153,5 @@ FUSES = {
};
-#endif
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-thefreeman-boost-fwaa-mp3432-hdr-dac-rgb.h b/hwdef-thefreeman-boost-fwaa-mp3432-hdr-dac-rgb.h
new file mode 100755
index 0000000..9126a1a
--- /dev/null
+++ b/hwdef-thefreeman-boost-fwaa-mp3432-hdr-dac-rgb.h
@@ -0,0 +1,187 @@
+// hwdef for thefreeman's boost FWAA driver 1.1 w/ MP3432, HDR DAC, RGB aux
+// Copyright (C) 2023 TBD (thefreeman), Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/* thefreeman’s FWAA AA/li-ion Boost driver based on MP3432 and attiny1616
+ * with high dynamic range and DAC control, AUX : RGB
+ * hardware versions : 1.0, 1.1
+ *
+ * Pin / Name / Function
+ * 1 PA2 BattLVL (ADC0 - AIN2)
+ * 2 PA3
+ * 3 GND GND
+ * 4 VCC VCC
+ * 5 PA4 BST EN: boost enable
+ * 6 PA5 HDR
+ * 7 PA6 DAC
+ * 8 PA7
+ * 9 PB5 B: blue aux LED
+ * 10 PB4 G: green aux LED
+ * 11 PB3 R: red aux LED
+ * 12 PB2 IN- NFET
+ * 13 PB1
+ * 14 PB0
+ * 15 PC0
+ * 16 PC1
+ * 17 PC2
+ * 18 PC3 e-switch
+ * 19 PA0 UDPI
+ * 20 PA1
+ *
+ * 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
+ */
+
+#define ATTINY 1616
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.c
+
+// allow using aux LEDs as extra channel modes
+#include "chan-rgbaux.h"
+
+// channel modes:
+// * 0. main LEDs
+// * 1+. aux RGB
+#define NUM_CHANNEL_MODES (1 + NUM_RGB_AUX_CHANNEL_MODES)
+enum CHANNEL_MODES {
+ CM_MAIN = 0,
+ RGB_AUX_ENUMS
+};
+
+#define DEFAULT_CHANNEL_MODE CM_MAIN
+
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b0000000000000001
+
+
+#define PWM_CHANNELS 1 // old, remove this
+
+#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
+
+// 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
+
+// BST enable
+#define BST_ENABLE_PIN PIN4_bp
+#define BST_ENABLE_PORT PORTA_OUT
+
+// HDR
+// turns on HDR FET for the high current range
+#define HDR_ENABLE_PIN PIN5_bp
+#define HDR_ENABLE_PORT PORTA_OUT
+
+// IN- NFET
+// pull high to force output to zero to eliminate the startup flash
+#define IN_NFET_DELAY_TIME 4 // (ms)
+#define IN_NFET_ENABLE_PIN PIN2_bp
+#define IN_NFET_ENABLE_PORT PORTB_OUT
+
+// e-switch
+#ifndef SWITCH_PIN
+#define SWITCH_PIN PIN3_bp
+#define SWITCH_PORT VPORTC.IN
+#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
+
+// 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 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
+
+// this driver allows for aux LEDs under the optic
+#define AUXLED_R_PIN PIN3_bp
+#define AUXLED_G_PIN PIN4_bp
+#define AUXLED_B_PIN PIN5_bp
+#define AUXLED_RGB_PORT PORTB // PORTA or PORTB or PORTC
+
+// 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 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 );
+
+ VPORTA.DIR = PIN4_bm // BST EN
+ | PIN5_bm // HDR
+ | PIN6_bm; // DAC
+ VPORTB.DIR = PIN2_bm // IN- NFET
+ | PIN3_bm // R
+ | PIN4_bm // G
+ | PIN5_bm; // B
+ //VPORTC.DIR = PIN0_bm | PIN1_bm;
+
+ // enable pullups on the input pins to reduce power
+ PORTA.PIN0CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN1CTRL = PORT_PULLUPEN_bm;
+ //PORTA.PIN2CTRL = PORT_PULLUPEN_bm; // BattLVL
+ PORTA.PIN3CTRL = PORT_PULLUPEN_bm;
+ //PORTA.PIN4CTRL = PORT_PULLUPEN_bm; // EN
+ //PORTA.PIN5CTRL = PORT_PULLUPEN_bm; // HDR
+ //PORTA.PIN6CTRL = PORT_PULLUPEN_bm; // DAC
+ PORTA.PIN7CTRL = PORT_PULLUPEN_bm;
+
+ PORTB.PIN0CTRL = PORT_PULLUPEN_bm;
+ PORTB.PIN1CTRL = PORT_PULLUPEN_bm;
+ //PORTB.PIN2CTRL = PORT_PULLUPEN_bm; // IN- NFET
+ //PORTB.PIN3CTRL = PORT_PULLUPEN_bm; // R
+ //PORTB.PIN4CTRL = PORT_PULLUPEN_bm; // G
+ //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // B
+
+ PORTC.PIN0CTRL = PORT_PULLUPEN_bm;
+ PORTC.PIN1CTRL = PORT_PULLUPEN_bm;
+ PORTC.PIN2CTRL = PORT_PULLUPEN_bm;
+ PORTC.PIN3CTRL = PORT_PULLUPEN_bm
+ | PORT_ISC_BOTHEDGES_gc; // e-switch
+
+ // 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
+
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.c b/hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.c
new file mode 100644
index 0000000..31feec9
--- /dev/null
+++ b/hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.c
@@ -0,0 +1,90 @@
+// thefreeman boost driver 2.1 output helper functions
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#include "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 = V055; // 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
+ PWM_DATATYPE dac_lvl = PWM_GET(pwm1_levels, level);
+ PWM_DATATYPE dac_vref = PWM_GET(pwm_tops, 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
+
+ PWM_DATATYPE dac_next = PWM_GET(pwm1_levels, gt);
+ PWM_DATATYPE vref_next = PWM_GET(pwm_tops, gt);
+
+ // different gear = full adjustment
+ if (vref_next != DAC_VREF) return true; // let parent set_level() for us
+
+ // same gear = small adjustment
+ GRADUAL_ADJUST_SIMPLE(dac_next, DAC_LVL);
+ if (dac_next == DAC_LVL) return true; // done
+
+ return false; // not done yet
+}
+
diff --git a/hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.h b/hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.h
new file mode 100644
index 0000000..1c35014
--- /dev/null
+++ b/hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.h
@@ -0,0 +1,181 @@
+// hwdef for thefreeman's boost driver 2.1 w/ MP3431, DAC, ARGB
+// Copyright (C) 2023 TBD (thefreeman), Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/* thefreeman’s Boost driver based on MP3431 and attiny1616
+ * with high dynamic range and DAC control, AUX : RGB + button
+ * hardware version : 2.0+
+ * compatible with BST20-FWxA v1.0 (no button LED A)
+ *
+ * Pin / Name / Function
+ * 1 PA2
+ * 2 PA3
+ * 3 GND GND
+ * 4 VCC VCC
+ * 5 PA4
+ * 6 PA5
+ * 7 PA6 DAC
+ * 8 PA7
+ * 9 PB5 IN- NFET
+ * 10 PB4 HDR
+ * 11 PB3 B: blue aux LED
+ * 12 PB2 G: green aux LED
+ * 13 PB1 R: red aux LED
+ * 14 PB0
+ * 15 PC0 boost enable
+ * 16 PC1 A: button LED
+ * 17 PC2 e-switch
+ * 18 PC3
+ * 19 PA0 UDPI
+ * 20 PA1
+ *
+ * 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
+ */
+
+#define ATTINY 1616
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.c
+
+// allow using aux LEDs as extra channel modes
+#include "chan-rgbaux.h"
+
+// channel modes:
+// * 0. main LEDs
+// * 1+. aux RGB
+#define NUM_CHANNEL_MODES (1 + NUM_RGB_AUX_CHANNEL_MODES)
+enum CHANNEL_MODES {
+ CM_MAIN = 0,
+ RGB_AUX_ENUMS
+};
+
+#define DEFAULT_CHANNEL_MODE CM_MAIN
+
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b0000000000000001
+
+
+#define PWM_CHANNELS 1 // old, remove this
+
+#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
+
+// 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
+
+// BST enable
+#define BST_ENABLE_PIN PIN0_bp
+#define BST_ENABLE_PORT PORTC_OUT
+
+// HDR
+// turns on HDR FET for the high current range
+#define HDR_ENABLE_PIN PIN4_bp
+#define HDR_ENABLE_PORT PORTB_OUT
+
+// IN- NFET
+// pull high to force output to zero to eliminate the startup flash
+#define IN_NFET_DELAY_TIME 8 // (ms)
+#define IN_NFET_ENABLE_PIN PIN5_bp
+#define IN_NFET_ENABLE_PORT PORTB_OUT
+
+// e-switch
+#ifndef SWITCH_PIN
+#define SWITCH_PIN PIN2_bp
+#define SWITCH_PORT VPORTC.IN
+#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
+#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 PIN1_bp
+#define AUXLED_G_PIN PIN2_bp
+#define AUXLED_B_PIN PIN3_bp
+#define AUXLED_RGB_PORT PORTB // PORTA or PORTB or PORTC
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+
+// A: button LED
+#ifndef BUTTON_LED_PIN
+#define BUTTON_LED_PIN PIN1_bp
+#define BUTTON_LED_PORT PORTC
+#endif
+
+
+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 );
+
+ VPORTA.DIR = PIN6_bm; // DAC
+ VPORTB.DIR = PIN1_bm // R
+ | PIN2_bm // G
+ | PIN3_bm // B
+ | PIN4_bm // HDR
+ | PIN5_bm; // IN- NFET
+ VPORTC.DIR = PIN0_bm // BST EN
+ | PIN1_bm; // A
+
+ // enable pullups on the input pins to reduce power
+ PORTA.PIN0CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN1CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN2CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN3CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN4CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN5CTRL = PORT_PULLUPEN_bm;
+ //PORTA.PIN6CTRL = PORT_PULLUPEN_bm; // DAC
+ PORTA.PIN7CTRL = PORT_PULLUPEN_bm;
+
+ PORTB.PIN0CTRL = PORT_PULLUPEN_bm;
+ //PORTB.PIN1CTRL = PORT_PULLUPEN_bm; // R
+ //PORTB.PIN2CTRL = PORT_PULLUPEN_bm; // G
+ //PORTB.PIN3CTRL = PORT_PULLUPEN_bm; // B
+ //PORTB.PIN4CTRL = PORT_PULLUPEN_bm; // HDR
+ //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // IN- NFET
+
+ //PORTC.PIN0CTRL = PORT_PULLUPEN_bm; // EN
+ //PORTC.PIN1CTRL = PORT_PULLUPEN_bm; // A
+ PORTC.PIN2CTRL = PORT_PULLUPEN_bm
+ | PORT_ISC_BOTHEDGES_gc; // e-switch
+ PORTC.PIN3CTRL = PORT_PULLUPEN_bm;
+
+ // 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
+
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-thefreeman-lin16dac.c b/hwdef-thefreeman-lin16dac.c
new file mode 100644
index 0000000..07c7f9e
--- /dev/null
+++ b/hwdef-thefreeman-lin16dac.c
@@ -0,0 +1,92 @@
+// thefreeman linear t1616 DAC driver helper functions
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#include "chan-aux.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
+ },
+ AUX_CHANNELS
+};
+
+
+void set_level_zero() {
+ DAC_LVL = 0; // DAC off
+ DAC_VREF = V055; // 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 opamp last
+ OPAMP_ENABLE_PORT &= ~(1 << OPAMP_ENABLE_PIN); // Opamp 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) {
+ #if 0 // unsure if this helps anything
+ uint8_t noflash = 0;
+
+ // when turning on from off, try to prevent a flash
+ if ((! actual_level) && (level < HDR_ENABLE_LEVEL_MIN)) {
+ noflash = 1;
+ }
+ #endif
+
+ // Opamp on first, to give it a few extra microseconds to spin up
+ 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);
+
+ // 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);
+
+ #if 0
+ if (noflash) {
+ // wait for flash prevention to finish
+ delay_4ms(OPAMP_ON_DELAY/4);
+ }
+ #endif
+
+ // 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;
+}
+
+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
+
+ PWM_DATATYPE dac_next = PWM_GET(pwm1_levels, gt);
+ PWM_DATATYPE vref_next = PWM_GET(pwm_tops, gt);
+
+ // different gear = full adjustment
+ if (vref_next != DAC_VREF) return true; // let parent set_level() for us
+
+ // same gear = small adjustment
+ GRADUAL_ADJUST_SIMPLE(dac_next, DAC_LVL);
+ if (dac_next == DAC_LVL) return true; // done
+
+ return false; // not done yet
+}
+
diff --git a/hwdef-thefreeman-lin16dac.h b/hwdef-thefreeman-lin16dac.h
index 9d6b145..c0d0638 100644
--- a/hwdef-thefreeman-lin16dac.h
+++ b/hwdef-thefreeman-lin16dac.h
@@ -1,53 +1,79 @@
-#ifndef HWDEF_THEFREEMAN_LIN18_H
-#define HWDEF_THEFREEMAN_LIN18_H
+// thefreeman's Linear 16 driver using DAC control
+// Copyright (C) 2021-2023 thefreeman, Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * PA6 - DAC for LED brightness control
+ * PA7 - Op-amp enable pin
+ * PB5 - Aux LED
+ * PB4 - Switch pin, internal pullup
+ * PB3 - HDR control, set High to enable the high power channel, set Low for low power
+ * Read voltage from VCC pin, has PFET so no drop
+ */
-/* thefreeman's Linear 16 driver using DAC control
-
-PA6 - DAC for LED brightness control
-PA7 - Op-amp enable pin
-PB5 - Aux LED
-PB4 - Switch pin, internal pullup
-PB3 - HDR control, set High to enable the high power channel, set Low for low power
-Read voltage from VCC pin, has PFET so no drop
+#define ATTINY 1616
+#include <avr/io.h>
-*/
+#define HWDEF_C_FILE hwdef-thefreeman-lin16dac.c
+// allow using aux LEDs as extra channel modes
+#include "chan-aux.h"
-#define LAYOUT_DEFINED
-
-#ifdef ATTINY
-#undef ATTINY
-#endif
-#define ATTINY 1616
-#include <avr/io.h>
+// channel modes:
+// * 0. main LEDs
+// * 1+. aux RGB
+#define NUM_CHANNEL_MODES 2
+enum CHANNEL_MODES {
+ CM_MAIN = 0,
+ CM_AUX
+};
-#define PWM_CHANNELS 1
-#define USE_DYN_PWM // dynamic frequency and speed
+#define DEFAULT_CHANNEL_MODE CM_MAIN
-#ifndef SWITCH_PIN
-#define SWITCH_PIN PIN4_bp
-#define SWITCH_PORT VPORTB.IN
-#define SWITCH_ISC_REG PORTB.PIN2CTRL
-#define SWITCH_VECT PORTB_PORT_vect
-#define SWITCH_INTFLG VPORTB.INTFLAGS
-#endif
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b0000000000000001
-#define PWM1_LVL DAC0.DATA // use this for DAC voltage output
+#define PWM_CHANNELS 1 // old, remove this
-// PWM parameters of both channels are tied together because they share a counter
-#define PWM1_TOP VREF.CTRLA // holds the TOP value for for variable-resolution PWM
+#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
-// For enabling / disabling the HDR high-range channel
-#define LED_ENABLE_PIN PIN3_bp
-#define LED_ENABLE_PORT PORTB_OUT
-#define LED_ENABLE_PIN_LEVEL_MIN 35
-#define LED_ENABLE_PIN_LEVEL_MAX 150
+// 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
+// Opamp enable
// For turning on and off the op-amp
-#define LED2_ENABLE_PIN PIN7_bp
-#define LED2_ENABLE_PORT PORTA_OUT
-#define LED2_ON_DELAY 80 // how many ms to delay turning on the lights after enabling the channel
+#define OPAMP_ENABLE_PIN PIN7_bp
+#define OPAMP_ENABLE_PORT PORTA_OUT
+// how many ms to delay turning on the lights after enabling the channel
+// (FIXME: 80 is long enough it's likely to cause bugs elsewhere,
+// as events stack up unhandled for 5 consecutive WDT ticks)
+#define OPAMP_ON_DELAY 80
+
+// HDR
+// turns on HDR FET for the high current range
+#define HDR_ENABLE_PIN PIN3_bp
+#define HDR_ENABLE_PORT PORTB_OUT
+
+// e-switch
+#define SWITCH_PIN PIN4_bp
+#define SWITCH_PORT VPORTB.IN
+#define SWITCH_ISC_REG PORTB.PIN2CTRL
+#define SWITCH_VECT PORTB_PORT_vect
+#define SWITCH_INTFLG VPORTB.INTFLAGS
// average drop across diode on this hardware
#ifndef VOLTAGE_FUDGE_FACTOR
@@ -55,22 +81,21 @@ Read voltage from VCC pin, has PFET so no drop
#endif
// lighted button
-#ifndef AUXLED_PIN
-#define AUXLED_PIN PIN5_bp
-#define AUXLED_PORT PORTB
-#endif
+#define AUXLED_PIN PIN5_bp
+#define AUXLED_PORT PORTB
-// with so many pins, doing this all with #ifdefs gets awkward...
-// ... so just hardcode it in each hwdef file instead
inline void hwdef_setup() {
// set up the system clock to run at 10 MHz instead of the default 3.33 MHz
- // TODO: for this DAC controlled-light, try to decrease the clock speed or use the ULP
- _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB, CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm );
-
- VPORTA.DIR = PIN6_bm | PIN7_bm;
- VPORTB.DIR = PIN3_bm;
+ // (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 );
+
+ VPORTA.DIR = PIN6_bm // DAC
+ | PIN7_bm; // Opamp
+ VPORTB.DIR = PIN3_bm; // HDR
//VPORTC.DIR = 0b00000000;
// enable pullups on the input pins to reduce power
@@ -85,9 +110,10 @@ inline void hwdef_setup() {
PORTB.PIN0CTRL = PORT_PULLUPEN_bm;
PORTB.PIN1CTRL = PORT_PULLUPEN_bm;
- PORTB.PIN2CTRL = PORT_PULLUPEN_bm;
+ PORTB.PIN2CTRL = PORT_PULLUPEN_bm;
//PORTB.PIN3CTRL = PORT_PULLUPEN_bm; // HDR channel selection
- PORTB.PIN4CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // switch
+ PORTB.PIN4CTRL = PORT_PULLUPEN_bm
+ | PORT_ISC_BOTHEDGES_gc; // e-switch
//PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // Aux LED
PORTC.PIN0CTRL = PORT_PULLUPEN_bm;
@@ -98,7 +124,8 @@ 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
- VREF.CTRLA |= VREF_DAC0REFSEL_2V5_gc; // also VREF_DAC0REFSEL_0V55_gc and VREF_DAC0REFSEL_1V1_gc and VREF_DAC0REFSEL_2V5_gc
+ // 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
@@ -106,4 +133,5 @@ inline void hwdef_setup() {
}
-#endif
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-wurkkos-ts10.c b/hwdef-wurkkos-ts10.c
new file mode 100644
index 0000000..06f5bac
--- /dev/null
+++ b/hwdef-wurkkos-ts10.c
@@ -0,0 +1,65 @@
+// Wurkkos TS10 PWM helper functions
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "chan-aux.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
+ },
+ { // aux LEDs
+ .set_level = set_level_aux,
+ .gradual_tick = gradual_tick_null
+ }
+};
+
+
+void set_level_zero() {
+ CH1_PWM = 0;
+ CH2_PWM = 0;
+ PWM_CNT = 0; // reset phase
+}
+
+// single set of LEDs with 2 stacked power channels, DDFET+1 or DDFET+linear
+void set_level_main(uint8_t level) {
+ PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level);
+ PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level);
+ // pulse frequency modulation, a.k.a. dynamic PWM
+ uint16_t top = PWM_GET16(pwm_tops, level);
+
+ CH1_PWM = ch1_pwm;
+ CH2_PWM = ch2_pwm;
+ // wait to sync the counter and avoid flashes
+ // (unnecessary w/ buffered registers)
+ //while(actual_level && (PWM_CNT > (top - 32))) {}
+ PWM_TOP = top;
+ // force reset phase when turning on from zero
+ // (because otherwise the initial response is inconsistent)
+ if (! actual_level) PWM_CNT = 0;
+}
+
+bool gradual_tick_main(uint8_t gt) {
+ PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt);
+ PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt);
+
+ GRADUAL_ADJUST_STACKED(pwm1, CH1_PWM, PWM_TOP_INIT);
+ GRADUAL_ADJUST_SIMPLE (pwm2, CH2_PWM);
+
+ if ( (pwm1 == CH1_PWM)
+ && (pwm2 == CH2_PWM)
+ ) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
diff --git a/hwdef-wurkkos-ts10.h b/hwdef-wurkkos-ts10.h
new file mode 100644
index 0000000..e693c99
--- /dev/null
+++ b/hwdef-wurkkos-ts10.h
@@ -0,0 +1,136 @@
+// Wurkkos TS10 driver layout
+// Copyright (C) 2021-2023 gchart, Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * (based on BLF Q8-t1616 driver layout)
+ * Driver pinout:
+ * eSwitch: PA5
+ * Aux LED: PB5
+ * PWM FET: PB0 (TCA0 WO0)
+ * PWM 1x7135: PB1 (TCA0 WO1)
+ * Voltage: VCC
+ */
+
+#define ATTINY 1616
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-wurkkos-ts10.c
+
+// allow using aux LEDs as extra channel modes
+#include "chan-aux.h"
+
+// channel modes:
+// * 0. FET+7135 stacked
+// * 1. aux LEDs
+#define NUM_CHANNEL_MODES 2
+enum CHANNEL_MODES {
+ CM_MAIN = 0,
+ CM_AUX
+};
+
+#define DEFAULT_CHANNEL_MODE CM_MAIN
+
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b00000001
+
+
+#define PWM_CHANNELS 2 // old, remove this
+
+#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255
+#define PWM_GET PWM_GET8
+#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255)
+#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255
+#define PWM1_DATATYPE uint8_t // 1x7135 ramp
+#define PWM2_DATATYPE uint8_t // DD FET ramp
+
+// PWM parameters of both channels are tied together because they share a counter
+#define PWM_TOP TCA0.SINGLE.PERBUF // holds the TOP value for for variable-resolution PWM
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+// not necessary when double-buffered "BUF" registers are used
+#define PWM_CNT TCA0.SINGLE.CNT // for resetting phase after each TOP adjustment
+
+// 1x7135 channel
+#define CH1_PIN PB1
+#define CH1_PWM TCA0.SINGLE.CMP1BUF // CMP1 is the output compare register for PB1
+
+// DD FET channel
+#define CH2_PIN PB0
+#define CH2_PWM TCA0.SINGLE.CMP0BUF // CMP0 is the output compare register for PB0
+
+// 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
+#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V
+#endif
+
+// front-facing aux LEDs
+#define AUXLED_PIN PIN5_bp
+#define AUXLED_PORT PORTB
+
+
+inline void hwdef_setup() {
+
+ // set up the system clock to run at 10 MHz instead of the default 3.33 MHz
+ _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB,
+ CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm );
+
+ //VPORTA.DIR = ...;
+ // Outputs
+ VPORTB.DIR = PIN0_bm // DD FET
+ | PIN1_bm // 7135
+ | PIN5_bm; // Aux LED
+ //VPORTC.DIR = ...;
+
+ // enable pullups on the unused pins to reduce power
+ PORTA.PIN0CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN1CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN2CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN3CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN4CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN5CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // eSwitch
+ PORTA.PIN6CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN7CTRL = PORT_PULLUPEN_bm;
+
+ //PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // FET channel
+ //PORTB.PIN1CTRL = PORT_PULLUPEN_bm; // 7135 channel
+ PORTB.PIN2CTRL = PORT_PULLUPEN_bm;
+ PORTB.PIN3CTRL = PORT_PULLUPEN_bm;
+ PORTB.PIN4CTRL = PORT_PULLUPEN_bm;
+ //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // Aux LED
+
+ PORTC.PIN0CTRL = PORT_PULLUPEN_bm;
+ PORTC.PIN1CTRL = PORT_PULLUPEN_bm;
+ PORTC.PIN2CTRL = PORT_PULLUPEN_bm;
+ PORTC.PIN3CTRL = PORT_PULLUPEN_bm;
+
+ // set up the PWM
+ // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf
+ // PB0 is TCA0:WO0, use TCA_SINGLE_CMP0EN_bm
+ // PB1 is TCA0:WO1, use TCA_SINGLE_CMP1EN_bm
+ // PB2 is TCA0:WO2, use TCA_SINGLE_CMP2EN_bm
+ // For Fast (Single Slope) PWM use TCA_SINGLE_WGMODE_SINGLESLOPE_gc
+ // For Phase Correct (Dual Slope) PWM use TCA_SINGLE_WGMODE_DSBOTTOM_gc
+ // See the manual for other pins, clocks, configs, portmux, etc
+ TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm
+ | TCA_SINGLE_CMP1EN_bm
+ | TCA_SINGLE_WGMODE_DSBOTTOM_gc;
+ TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc
+ | TCA_SINGLE_ENABLE_bm;
+
+ PWM_TOP = PWM_TOP_INIT;
+
+}
+
+
+#define LAYOUT_DEFINED
+
diff --git a/hwdef-wurkkos-ts25.c b/hwdef-wurkkos-ts25.c
new file mode 100644
index 0000000..26c9b0d
--- /dev/null
+++ b/hwdef-wurkkos-ts25.c
@@ -0,0 +1,62 @@
+// Wurkkos TS25 PWM helper functions
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "chan-rgbaux.c"
+
+void set_level_zero();
+
+void set_level_main(uint8_t level);
+bool gradual_tick_main(uint8_t gt);
+
+
+Channel channels[] = {
+ { // channel 1 only
+ .set_level = set_level_main,
+ .gradual_tick = gradual_tick_main
+ },
+ RGB_AUX_CHANNELS
+};
+
+
+void set_level_zero() {
+ CH1_PWM = 0;
+ CH2_PWM = 0;
+ PWM_CNT = 0; // reset phase
+}
+
+// single set of LEDs with 2 stacked power channels, DDFET+1 or DDFET+linear
+void set_level_main(uint8_t level) {
+ PWM_DATATYPE ch1_pwm = PWM_GET(pwm1_levels, level);
+ PWM_DATATYPE ch2_pwm = PWM_GET(pwm2_levels, level);
+ // pulse frequency modulation, a.k.a. dynamic PWM
+ uint16_t top = PWM_GET16(pwm_tops, level);
+
+ CH1_PWM = ch1_pwm;
+ CH2_PWM = ch2_pwm;
+ // wait to sync the counter and avoid flashes
+ // (unnecessary w/ buffered registers)
+ //while(actual_level && (PWM_CNT > (top - 32))) {}
+ PWM_TOP = top;
+ // force reset phase when turning on from zero
+ // (because otherwise the initial response is inconsistent)
+ if (! actual_level) PWM_CNT = 0;
+}
+
+bool gradual_tick_main(uint8_t gt) {
+ PWM_DATATYPE pwm1 = PWM_GET(pwm1_levels, gt);
+ PWM_DATATYPE pwm2 = PWM_GET(pwm2_levels, gt);
+
+ GRADUAL_ADJUST_STACKED(pwm1, CH1_PWM, PWM_TOP_INIT);
+ GRADUAL_ADJUST_SIMPLE (pwm2, CH2_PWM);
+
+ if ( (pwm1 == CH1_PWM)
+ && (pwm2 == CH2_PWM)
+ ) {
+ return true; // done
+ }
+ return false; // not done yet
+}
+
diff --git a/hwdef-wurkkos-ts25.h b/hwdef-wurkkos-ts25.h
new file mode 100644
index 0000000..5eed219
--- /dev/null
+++ b/hwdef-wurkkos-ts25.h
@@ -0,0 +1,144 @@
+// Wurkkos TS25 driver layout
+// Copyright (C) 2022-2023 (FIXME)
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
+ * Driver pinout:
+ * eSwitch: PA5
+ * PWM FET: PB0 (TCA0 WO0)
+ * PWM 1x7135: PB1 (TCA0 WO1)
+ * Voltage: VCC
+ * Aux Red: PC2
+ * Aux Green: PC3
+ * Aux Blue: PC1
+ */
+
+#define ATTINY 1616
+#include <avr/io.h>
+
+#define HWDEF_C_FILE hwdef-wurkkos-ts25.c
+
+// allow using aux LEDs as extra channel modes
+#include "chan-rgbaux.h"
+
+// channel modes:
+// * 0. FET+7135 stacked
+// * 1+. aux RGB
+#define NUM_CHANNEL_MODES (1 + NUM_RGB_AUX_CHANNEL_MODES)
+enum CHANNEL_MODES {
+ CM_MAIN = 0,
+ RGB_AUX_ENUMS
+};
+
+#define DEFAULT_CHANNEL_MODE CM_MAIN
+
+// right-most bit first, modes are in fedcba9876543210 order
+#define CHANNEL_MODES_ENABLED 0b0000000000000001
+
+
+#define PWM_CHANNELS 2 // old, remove this
+
+#define PWM_BITS 16 // dynamic 16-bit, but never goes over 255
+#define PWM_GET PWM_GET8
+#define PWM_DATATYPE uint16_t // is used for PWM_TOPS (which goes way over 255)
+#define PWM_DATATYPE2 uint16_t // only needs 32-bit if ramp values go over 255
+#define PWM1_DATATYPE uint8_t // 1x7135 ramp
+#define PWM2_DATATYPE uint8_t // DD FET ramp
+
+// PWM parameters of both channels are tied together because they share a counter
+#define PWM_TOP TCA0.SINGLE.PERBUF // holds the TOP value for for variable-resolution PWM
+#define PWM_TOP_INIT 255 // highest value used in top half of ramp
+// not necessary when double-buffered "BUF" registers are used
+#define PWM_CNT TCA0.SINGLE.CNT // for resetting phase after each TOP adjustment
+
+// 1x7135 channel
+#define CH1_PIN PB1
+#define CH1_PWM TCA0.SINGLE.CMP1BUF // CMP1 is the output compare register for PB1
+
+// DD FET channel
+#define CH2_PIN PB0
+#define CH2_PWM TCA0.SINGLE.CMP0BUF // CMP0 is the output compare register for PB0
+
+// 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
+#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V
+#endif
+
+// this driver allows for aux LEDs under the optic
+#define AUXLED_R_PIN PIN2_bp // pin 2
+#define AUXLED_G_PIN PIN3_bp // pin 3
+#define AUXLED_B_PIN PIN1_bp // pin 1
+#define AUXLED_RGB_PORT PORTC // PORTA or PORTB or PORTC
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+
+
+inline void hwdef_setup() {
+
+ // set up the system clock to run at 10 MHz instead of the default 3.33 MHz
+ _PROTECTED_WRITE( CLKCTRL.MCLKCTRLB,
+ CLKCTRL_PDIV_2X_gc | CLKCTRL_PEN_bm );
+
+ //VPORTA.DIR = ...;
+ // Outputs: PWMs
+ VPORTB.DIR = PIN0_bm
+ | PIN1_bm;
+ // RGB aux LEDs
+ VPORTC.DIR = PIN1_bm
+ | PIN2_bm
+ | PIN3_bm;
+
+ // enable pullups on the unused pins to reduce power
+ PORTA.PIN0CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN1CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN2CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN3CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN4CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN5CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // eSwitch
+ PORTA.PIN6CTRL = PORT_PULLUPEN_bm;
+ PORTA.PIN7CTRL = PORT_PULLUPEN_bm;
+
+ //PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // FET channel
+ //PORTB.PIN1CTRL = PORT_PULLUPEN_bm; // 7135 channel
+ PORTB.PIN2CTRL = PORT_PULLUPEN_bm;
+ PORTB.PIN3CTRL = PORT_PULLUPEN_bm;
+ PORTB.PIN4CTRL = PORT_PULLUPEN_bm;
+ PORTB.PIN5CTRL = PORT_PULLUPEN_bm;
+
+ PORTC.PIN0CTRL = PORT_PULLUPEN_bm;
+ //PORTC.PIN1CTRL = PORT_PULLUPEN_bm; // RGB Aux
+ //PORTC.PIN2CTRL = PORT_PULLUPEN_bm; // RGB Aux
+ //PORTC.PIN3CTRL = PORT_PULLUPEN_bm; // RGB Aux
+
+ // set up the PWM
+ // https://ww1.microchip.com/downloads/en/DeviceDoc/ATtiny1614-16-17-DataSheet-DS40002204A.pdf
+ // PB0 is TCA0:WO0, use TCA_SINGLE_CMP0EN_bm
+ // PB1 is TCA0:WO1, use TCA_SINGLE_CMP1EN_bm
+ // PB2 is TCA0:WO2, use TCA_SINGLE_CMP2EN_bm
+ // For Fast (Single Slope) PWM use TCA_SINGLE_WGMODE_SINGLESLOPE_gc
+ // For Phase Correct (Dual Slope) PWM use TCA_SINGLE_WGMODE_DSBOTTOM_gc
+ // See the manual for other pins, clocks, configs, portmux, etc
+ TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm
+ | TCA_SINGLE_CMP1EN_bm
+ | TCA_SINGLE_WGMODE_DSBOTTOM_gc;
+ TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc
+ | TCA_SINGLE_ENABLE_bm;
+
+ PWM_TOP = PWM_TOP_INIT;
+
+}
+
+
+#define LAYOUT_DEFINED
+