From 7cb4fe0944b839f28dfd96a88a772cd6a8b58019 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Thu, 2 Nov 2023 17:16:25 -0600 Subject: reorganized project files (part 1) (just moved files, didn't change the contents yet, and nothing will work without updating #includes and build scripts and stuff) --- ChangeLog.md | 245 + MODELS | 85 + bin/build-all.sh | 57 + bin/models.py | 71 + bin/steps.py | 51 + docs/anduril-manual.txt | 994 ++++ docs/anduril-ui.png | Bin 0 -> 282516 bytes docs/anduril.svg | 4921 ++++++++++++++++++++ fsm/COPYING | 674 +++ fsm/adc.c | 573 +++ fsm/adc.h | 112 + fsm/chan-aux.c | 11 + fsm/chan-aux.h | 25 + fsm/chan-rgbaux.c | 35 + fsm/chan-rgbaux.h | 72 + fsm/channels.c | 357 ++ fsm/channels.h | 141 + fsm/eeprom.c | 112 + fsm/eeprom.h | 52 + fsm/events.c | 198 + fsm/events.h | 221 + fsm/main.c | 211 + fsm/main.h | 10 + fsm/misc.c | 312 ++ fsm/misc.h | 68 + fsm/pcint.c | 96 + fsm/pcint.h | 15 + fsm/ramping.c | 259 ++ fsm/ramping.h | 167 + fsm/random.c | 16 + fsm/random.h | 12 + fsm/spaghetti-monster.h | 75 + fsm/spaghetti-monster.txt | 325 ++ fsm/standby.c | 105 + fsm/standby.h | 68 + fsm/states.c | 105 + fsm/states.h | 37 + fsm/wdt.c | 197 + fsm/wdt.h | 20 + hw/BRANDS | 11 + hw/fireflies/e01/cfg.h | 57 + hw/fireflies/pl47-219/cfg.h | 15 + hw/fireflies/pl47/cfg.h | 79 + hw/fireflies/pl47/hwdef.h | 26 + hw/fireflies/pl47g2/cfg.h | 67 + hw/fireflies/rot66-219/cfg.h | 26 + hw/fireflies/rot66/cfg.h | 65 + hw/fireflies/rot66/hwdef.h | 24 + hw/fireflies/rot66g2/cfg.h | 59 + hw/gchart/fet1-t1616/cfg.h | 63 + hw/gchart/fet1-t1616/hwdef.h | 134 + hw/hank/emisar-2ch-fet/cfg.h | 113 + hw/hank/emisar-2ch-fet/hwdef.c | 206 + hw/hank/emisar-2ch-fet/hwdef.h | 209 + hw/hank/emisar-2ch/cfg.h | 103 + hw/hank/emisar-2ch/hwdef.c | 193 + hw/hank/emisar-2ch/hwdef.h | 210 + hw/hank/emisar-d1/cfg.h | 20 + hw/hank/emisar-d18-219/cfg.h | 18 + hw/hank/emisar-d18/cfg.h | 61 + hw/hank/emisar-d18/hwdef.h | 101 + hw/hank/emisar-d1s/cfg.h | 23 + hw/hank/emisar-d1v2-7135-fet/cfg.h | 32 + hw/hank/emisar-d1v2-linear-fet/cfg.h | 30 + hw/hank/emisar-d1v2-nofet/cfg.h | 24 + hw/hank/emisar-d4-219/cfg.h | 17 + hw/hank/emisar-d4/cfg.h | 48 + hw/hank/emisar-d4/hwdef.c | 59 + hw/hank/emisar-d4/hwdef.h | 104 + hw/hank/emisar-d4k-3ch/cfg.h | 106 + hw/hank/emisar-d4k-3ch/hwdef.c | 362 ++ hw/hank/emisar-d4k-3ch/hwdef.h | 248 + hw/hank/emisar-d4s-219/cfg.h | 17 + hw/hank/emisar-d4s/cfg.h | 49 + hw/hank/emisar-d4s/hwdef.h | 13 + hw/hank/emisar-d4sv2-219/cfg.h | 18 + hw/hank/emisar-d4sv2/cfg.h | 81 + hw/hank/emisar-d4sv2/hwdef.c | 67 + hw/hank/emisar-d4sv2/hwdef.h | 179 + hw/hank/emisar-d4v2-219/cfg.h | 17 + hw/hank/emisar-d4v2-nofet/cfg.h | 62 + hw/hank/emisar-d4v2-nofet/hwdef.c | 55 + hw/hank/emisar-d4v2/cfg.h | 64 + hw/hank/emisar-d4v2/hwdef.c | 61 + hw/hank/emisar-d4v2/hwdef.h | 171 + hw/hank/hank-cfg.h | 30 + hw/hank/noctigon-dm11-boost/cfg.h | 91 + hw/hank/noctigon-dm11-boost/hwdef.c | 97 + hw/hank/noctigon-dm11-boost/hwdef.h | 206 + hw/hank/noctigon-dm11-nofet/cfg.h | 49 + hw/hank/noctigon-dm11-sbt90/cfg.h | 46 + hw/hank/noctigon-dm11/cfg.h | 88 + hw/hank/noctigon-dm11/hwdef.h | 183 + hw/hank/noctigon-k1-boost/cfg.h | 96 + hw/hank/noctigon-k1-boost/hwdef.h | 188 + hw/hank/noctigon-k1-sbt90/cfg.h | 96 + hw/hank/noctigon-k1-sbt90/hwdef.h | 177 + hw/hank/noctigon-k1/cfg.h | 83 + hw/hank/noctigon-k1/hwdef.c | 65 + hw/hank/noctigon-k1/hwdef.h | 170 + hw/hank/noctigon-k9.3-219/cfg.h | 15 + hw/hank/noctigon-k9.3-nofet/cfg.h | 12 + hw/hank/noctigon-k9.3/cfg.h | 111 + hw/hank/noctigon-kr4-219/cfg.h | 17 + hw/hank/noctigon-kr4-219b/cfg.h | 17 + hw/hank/noctigon-kr4-2ch/cfg.h | 16 + hw/hank/noctigon-kr4-2ch/hwdef.h | 47 + hw/hank/noctigon-kr4-boost/cfg.h | 16 + hw/hank/noctigon-kr4-boost/hwdef.h | 57 + hw/hank/noctigon-kr4-nofet/cfg.h | 66 + hw/hank/noctigon-kr4-nofet/hwdef.c | 60 + hw/hank/noctigon-kr4/cfg.h | 89 + hw/hank/noctigon-kr4/hwdef.c | 63 + hw/hank/noctigon-kr4/hwdef.h | 194 + hw/hank/noctigon-m44/cfg.h | 134 + hw/hank/noctigon-m44/hwdef.c | 262 ++ hw/hank/noctigon-m44/hwdef.h | 206 + hw/lumintop/blf-gt-mini/cfg.h | 25 + hw/lumintop/blf-gt-mini/hwdef.h | 14 + hw/lumintop/blf-gt/cfg.h | 62 + hw/lumintop/blf-gt/hwdef.h | 102 + hw/lumintop/fw3a-219/cfg.h | 16 + hw/lumintop/fw3a-nofet/cfg.h | 54 + hw/lumintop/fw3a/cfg.h | 54 + hw/lumintop/fw3a/hwdef.c | 71 + hw/lumintop/fw3a/hwdef.h | 105 + hw/lumintop/fw3x-lume1/cfg.h | 81 + hw/lumintop/fw3x-lume1/hwdef.c | 60 + hw/lumintop/fw3x-lume1/hwdef.h | 202 + hw/mateminco/mf01-mini/cfg.h | 74 + hw/mateminco/mf01-mini/hwdef.h | 100 + hw/mateminco/mf01s/cfg.h | 61 + hw/mateminco/mf01s/hwdef.h | 102 + hw/mateminco/mt35-mini/cfg.h | 65 + hw/mateminco/mt35-mini/hwdef.h | 93 + hw/sofirn/blf-lt1-t1616/cfg.h | 110 + hw/sofirn/blf-lt1-t1616/hwdef.c | 210 + hw/sofirn/blf-lt1-t1616/hwdef.h | 161 + hw/sofirn/blf-lt1/cfg.h | 105 + hw/sofirn/blf-lt1/hwdef.c | 204 + hw/sofirn/blf-lt1/hwdef.h | 114 + hw/sofirn/blf-q8-t1616/cfg.h | 82 + hw/sofirn/blf-q8-t1616/hwdef.h | 138 + hw/sofirn/blf-q8/cfg.h | 71 + hw/sofirn/blf-q8/hwdef.h | 20 + hw/sofirn/lt1s-pro/cfg.h | 125 + hw/sofirn/lt1s-pro/hwdef.c | 237 + hw/sofirn/lt1s-pro/hwdef.h | 151 + hw/sofirn/sc21-pro/cfg.h | 14 + hw/sofirn/sp10-pro/cfg.h | 81 + hw/sofirn/sp10-pro/hwdef.c | 63 + hw/sofirn/sp10-pro/hwdef.h | 157 + hw/sofirn/sp36-t1616/cfg.h | 37 + hw/sofirn/sp36/cfg.h | 36 + hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/cfg.h | 116 + .../boost-fwaa-mp3432-hdr-dac-rgb/hwdef.h | 187 + hw/thefreeman/boost21-mp3431-hdr-dac-argb/cfg.h | 119 + hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.c | 90 + hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.h | 181 + hw/thefreeman/lin16dac/cfg.h | 61 + hw/thefreeman/lin16dac/hwdef.c | 92 + hw/thefreeman/lin16dac/hwdef.h | 137 + hw/wurkkos/fc13/cfg.h | 17 + hw/wurkkos/ts10-rgbaux/cfg.h | 85 + hw/wurkkos/ts10/cfg.h | 85 + hw/wurkkos/ts10/hwdef.c | 65 + hw/wurkkos/ts10/hwdef.h | 136 + hw/wurkkos/ts11/cfg.h | 19 + hw/wurkkos/ts25/cfg.h | 87 + hw/wurkkos/ts25/hwdef.c | 62 + hw/wurkkos/ts25/hwdef.h | 144 + hw/wurkkos/wurkkos-cfg.h | 26 + hwdef-FET_7135.h | 38 - hwdef-Ferrero_Rocher.h | 18 - hwdef-TK_Saber.h | 37 - hwdef-Tripledown.h | 39 - hwdef-blf-gt-mini.h | 14 - hwdef-blf-gt.h | 102 - hwdef-blf-lt1-t1616.c | 210 - hwdef-blf-lt1-t1616.h | 161 - hwdef-blf-lt1.c | 204 - hwdef-blf-lt1.h | 114 - hwdef-blf-q8-t1616.h | 138 - hwdef-blf-q8.h | 20 - hwdef-emisar-2ch-fet.c | 206 - hwdef-emisar-2ch-fet.h | 209 - hwdef-emisar-2ch.c | 193 - hwdef-emisar-2ch.h | 210 - hwdef-emisar-d18.h | 101 - hwdef-emisar-d4.c | 59 - hwdef-emisar-d4.h | 104 - hwdef-emisar-d4k-3ch.c | 362 -- hwdef-emisar-d4k-3ch.h | 248 - hwdef-emisar-d4s.h | 13 - hwdef-emisar-d4sv2.c | 67 - hwdef-emisar-d4sv2.h | 179 - hwdef-emisar-d4v2-nofet.c | 55 - hwdef-emisar-d4v2.c | 61 - hwdef-emisar-d4v2.h | 171 - hwdef-ff-pl47.h | 26 - hwdef-ff-rot66.h | 24 - hwdef-fw3a.c | 71 - hwdef-fw3a.h | 105 - hwdef-fw3x-lume1.c | 60 - hwdef-fw3x-lume1.h | 202 - hwdef-gchart-fet1-t1616.h | 134 - hwdef-mateminco-mf01-mini.h | 100 - hwdef-mateminco-mf01s.h | 102 - hwdef-mateminco-mt35-mini.h | 93 - hwdef-nanjg.h | 22 - hwdef-noctigon-dm11-boost.c | 97 - hwdef-noctigon-dm11-boost.h | 206 - hwdef-noctigon-dm11.h | 183 - hwdef-noctigon-k1-boost.h | 188 - hwdef-noctigon-k1-sbt90.h | 177 - hwdef-noctigon-k1.c | 65 - hwdef-noctigon-k1.h | 170 - hwdef-noctigon-kr4-2ch.h | 47 - hwdef-noctigon-kr4-boost.h | 57 - hwdef-noctigon-kr4-nofet.c | 60 - hwdef-noctigon-kr4.c | 63 - hwdef-noctigon-kr4.h | 194 - hwdef-noctigon-m44.c | 262 -- hwdef-noctigon-m44.h | 206 - hwdef-sofirn-lt1s-pro.c | 237 - hwdef-sofirn-lt1s-pro.h | 151 - hwdef-sofirn-sp10-pro.c | 63 - hwdef-sofirn-sp10-pro.h | 157 - hwdef-thefreeman-boost-fwaa-mp3432-hdr-dac-rgb.h | 187 - hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.c | 90 - hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.h | 181 - hwdef-thefreeman-lin16dac.c | 92 - hwdef-thefreeman-lin16dac.h | 137 - hwdef-wurkkos-ts10.c | 65 - hwdef-wurkkos-ts10.h | 136 - hwdef-wurkkos-ts25.c | 62 - hwdef-wurkkos-ts25.h | 144 - spaghetti-monster/COPYING | 674 --- spaghetti-monster/anduril/BRANDS | 11 - spaghetti-monster/anduril/ChangeLog.md | 245 - spaghetti-monster/anduril/MODELS | 85 - spaghetti-monster/anduril/Makefile | 14 - spaghetti-monster/anduril/anduril-manual.txt | 994 ---- spaghetti-monster/anduril/anduril-ui.png | Bin 282516 -> 0 bytes spaghetti-monster/anduril/anduril.c | 396 -- spaghetti-monster/anduril/anduril.svg | 4921 -------------------- spaghetti-monster/anduril/anduril.txt | 191 - spaghetti-monster/anduril/aux-leds.c | 210 - spaghetti-monster/anduril/aux-leds.h | 65 - spaghetti-monster/anduril/battcheck-mode-fsm.h | 18 - spaghetti-monster/anduril/battcheck-mode.c | 82 - spaghetti-monster/anduril/battcheck-mode.h | 12 - spaghetti-monster/anduril/beacon-mode.c | 53 - spaghetti-monster/anduril/beacon-mode.h | 9 - spaghetti-monster/anduril/build-all.sh | 57 - spaghetti-monster/anduril/candle-mode.c | 136 - spaghetti-monster/anduril/candle-mode.h | 13 - spaghetti-monster/anduril/cfg-blf-gt-mini.h | 25 - spaghetti-monster/anduril/cfg-blf-gt.h | 62 - spaghetti-monster/anduril/cfg-blf-lantern-t1616.h | 110 - spaghetti-monster/anduril/cfg-blf-lantern.h | 105 - spaghetti-monster/anduril/cfg-blf-q8-t1616.h | 82 - spaghetti-monster/anduril/cfg-blf-q8.h | 71 - spaghetti-monster/anduril/cfg-emisar-2ch-fet.h | 113 - spaghetti-monster/anduril/cfg-emisar-2ch.h | 103 - spaghetti-monster/anduril/cfg-emisar-d1.h | 20 - spaghetti-monster/anduril/cfg-emisar-d18-219.h | 18 - spaghetti-monster/anduril/cfg-emisar-d18.h | 61 - spaghetti-monster/anduril/cfg-emisar-d1s.h | 23 - .../anduril/cfg-emisar-d1v2-7135-fet.h | 32 - .../anduril/cfg-emisar-d1v2-linear-fet.h | 30 - spaghetti-monster/anduril/cfg-emisar-d1v2-nofet.h | 24 - spaghetti-monster/anduril/cfg-emisar-d4-219c.h | 17 - spaghetti-monster/anduril/cfg-emisar-d4.h | 48 - spaghetti-monster/anduril/cfg-emisar-d4k-3ch.h | 106 - spaghetti-monster/anduril/cfg-emisar-d4s-219c.h | 17 - spaghetti-monster/anduril/cfg-emisar-d4s.h | 49 - spaghetti-monster/anduril/cfg-emisar-d4sv2-219.h | 18 - spaghetti-monster/anduril/cfg-emisar-d4sv2.h | 81 - spaghetti-monster/anduril/cfg-emisar-d4v2-219.h | 17 - spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h | 62 - spaghetti-monster/anduril/cfg-emisar-d4v2.h | 64 - spaghetti-monster/anduril/cfg-ff-e01.h | 57 - spaghetti-monster/anduril/cfg-ff-pl47-219.h | 15 - spaghetti-monster/anduril/cfg-ff-pl47.h | 79 - spaghetti-monster/anduril/cfg-ff-pl47g2.h | 67 - spaghetti-monster/anduril/cfg-ff-rot66-219.h | 26 - spaghetti-monster/anduril/cfg-ff-rot66.h | 65 - spaghetti-monster/anduril/cfg-ff-rot66g2.h | 59 - spaghetti-monster/anduril/cfg-fw3a-219.h | 16 - spaghetti-monster/anduril/cfg-fw3a-nofet.h | 54 - spaghetti-monster/anduril/cfg-fw3a.h | 54 - spaghetti-monster/anduril/cfg-fw3x-lume1.h | 81 - spaghetti-monster/anduril/cfg-gchart-fet1-t1616.h | 63 - .../anduril/cfg-mateminco-mf01-mini.h | 74 - spaghetti-monster/anduril/cfg-mateminco-mf01s.h | 61 - .../anduril/cfg-mateminco-mt35-mini.h | 65 - .../anduril/cfg-noctigon-dm11-boost.h | 91 - .../anduril/cfg-noctigon-dm11-nofet.h | 49 - .../anduril/cfg-noctigon-dm11-sbt90.h | 46 - spaghetti-monster/anduril/cfg-noctigon-dm11.h | 88 - spaghetti-monster/anduril/cfg-noctigon-k1-boost.h | 96 - spaghetti-monster/anduril/cfg-noctigon-k1-sbt90.h | 96 - spaghetti-monster/anduril/cfg-noctigon-k1.h | 83 - spaghetti-monster/anduril/cfg-noctigon-k9.3-219.h | 15 - .../anduril/cfg-noctigon-k9.3-nofet.h | 12 - spaghetti-monster/anduril/cfg-noctigon-k9.3.c | 108 - spaghetti-monster/anduril/cfg-noctigon-k9.3.h | 111 - spaghetti-monster/anduril/cfg-noctigon-kr4-219.h | 17 - spaghetti-monster/anduril/cfg-noctigon-kr4-219b.h | 17 - spaghetti-monster/anduril/cfg-noctigon-kr4-2ch.h | 16 - spaghetti-monster/anduril/cfg-noctigon-kr4-boost.h | 16 - spaghetti-monster/anduril/cfg-noctigon-kr4-nofet.h | 66 - spaghetti-monster/anduril/cfg-noctigon-kr4.h | 89 - spaghetti-monster/anduril/cfg-noctigon-m44.h | 134 - spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h | 125 - spaghetti-monster/anduril/cfg-sofirn-sc21-pro.h | 14 - spaghetti-monster/anduril/cfg-sofirn-sp10-pro.h | 81 - spaghetti-monster/anduril/cfg-sofirn-sp36-t1616.h | 37 - spaghetti-monster/anduril/cfg-sofirn-sp36.h | 36 - .../anduril/cfg-thefreeman-boost-fwaa.h | 116 - .../anduril/cfg-thefreeman-boost21-6a.h | 119 - .../anduril/cfg-thefreeman-lin16dac.h | 61 - spaghetti-monster/anduril/cfg-wurkkos-fc13.h | 17 - .../anduril/cfg-wurkkos-ts10-rgbaux.h | 85 - spaghetti-monster/anduril/cfg-wurkkos-ts10.h | 85 - spaghetti-monster/anduril/cfg-wurkkos-ts11.h | 19 - spaghetti-monster/anduril/cfg-wurkkos-ts25.h | 87 - spaghetti-monster/anduril/channel-modes.c | 237 - spaghetti-monster/anduril/channel-modes.h | 26 - spaghetti-monster/anduril/config-default.h | 207 - spaghetti-monster/anduril/config-mode.c | 196 - spaghetti-monster/anduril/config-mode.h | 24 - spaghetti-monster/anduril/factory-reset-fsm.h | 10 - spaghetti-monster/anduril/factory-reset.c | 73 - spaghetti-monster/anduril/factory-reset.h | 8 - spaghetti-monster/anduril/ff-strobe-modes.c | 62 - spaghetti-monster/anduril/ff-strobe-modes.h | 15 - spaghetti-monster/anduril/hank-cfg.h | 30 - spaghetti-monster/anduril/load-save-config-fsm.h | 139 - spaghetti-monster/anduril/load-save-config.c | 33 - spaghetti-monster/anduril/load-save-config.h | 173 - spaghetti-monster/anduril/lockout-mode-fsm.h | 11 - spaghetti-monster/anduril/lockout-mode.c | 219 - spaghetti-monster/anduril/lockout-mode.h | 16 - spaghetti-monster/anduril/misc.c | 42 - spaghetti-monster/anduril/misc.h | 10 - spaghetti-monster/anduril/models.py | 71 - spaghetti-monster/anduril/momentary-mode.c | 67 - spaghetti-monster/anduril/momentary-mode.h | 11 - spaghetti-monster/anduril/off-mode.c | 384 -- spaghetti-monster/anduril/off-mode.h | 12 - spaghetti-monster/anduril/ramp-mode-fsm.h | 38 - spaghetti-monster/anduril/ramp-mode.c | 741 --- spaghetti-monster/anduril/ramp-mode.h | 224 - spaghetti-monster/anduril/smooth-steps.c | 47 - spaghetti-monster/anduril/smooth-steps.h | 19 - spaghetti-monster/anduril/sos-mode.c | 56 - spaghetti-monster/anduril/sos-mode.h | 11 - spaghetti-monster/anduril/steps.py | 51 - spaghetti-monster/anduril/strobe-modes-fsm.h | 55 - spaghetti-monster/anduril/strobe-modes.c | 332 -- spaghetti-monster/anduril/strobe-modes.h | 71 - spaghetti-monster/anduril/sunset-timer.c | 60 - spaghetti-monster/anduril/sunset-timer.h | 17 - spaghetti-monster/anduril/tactical-mode.c | 109 - spaghetti-monster/anduril/tactical-mode.h | 22 - spaghetti-monster/anduril/tempcheck-mode.c | 56 - spaghetti-monster/anduril/tempcheck-mode.h | 12 - spaghetti-monster/anduril/tint-ramping.c | 86 - spaghetti-monster/anduril/tint-ramping.h | 21 - spaghetti-monster/anduril/version-check-mode.c | 31 - spaghetti-monster/anduril/version-check-mode.h | 19 - spaghetti-monster/anduril/version.h | 4 - spaghetti-monster/anduril/wurkkos-cfg.h | 26 - spaghetti-monster/baton/baton.c | 188 - spaghetti-monster/baton/baton.txt | 21 - spaghetti-monster/chan-aux.c | 11 - spaghetti-monster/chan-aux.h | 25 - spaghetti-monster/chan-rgbaux.c | 35 - spaghetti-monster/chan-rgbaux.h | 72 - spaghetti-monster/darkhorse/darkhorse.c | 367 -- spaghetti-monster/fireflies-ui/Makefile | 7 - spaghetti-monster/fireflies-ui/build-all.sh | 13 - spaghetti-monster/fireflies-ui/cfg-ff-e01.h | 44 - spaghetti-monster/fireflies-ui/cfg-ff-e07-2.h | 27 - spaghetti-monster/fireflies-ui/fireflies-ui.c | 2386 ---------- spaghetti-monster/fsm-adc.c | 573 --- spaghetti-monster/fsm-adc.h | 112 - spaghetti-monster/fsm-channels.c | 357 -- spaghetti-monster/fsm-channels.h | 141 - spaghetti-monster/fsm-eeprom.c | 112 - spaghetti-monster/fsm-eeprom.h | 52 - spaghetti-monster/fsm-events.c | 198 - spaghetti-monster/fsm-events.h | 221 - spaghetti-monster/fsm-main.c | 211 - spaghetti-monster/fsm-main.h | 10 - spaghetti-monster/fsm-misc.c | 312 -- spaghetti-monster/fsm-misc.h | 68 - spaghetti-monster/fsm-pcint.c | 96 - spaghetti-monster/fsm-pcint.h | 15 - spaghetti-monster/fsm-ramping.c | 259 -- spaghetti-monster/fsm-ramping.h | 167 - spaghetti-monster/fsm-random.c | 16 - spaghetti-monster/fsm-random.h | 12 - spaghetti-monster/fsm-standby.c | 105 - spaghetti-monster/fsm-standby.h | 68 - spaghetti-monster/fsm-states.c | 105 - spaghetti-monster/fsm-states.h | 37 - spaghetti-monster/fsm-wdt.c | 197 - spaghetti-monster/fsm-wdt.h | 20 - spaghetti-monster/meteor/meteor.c | 556 --- spaghetti-monster/momentary/momentary.c | 80 - spaghetti-monster/ramping-ui/ramping-ui.c | 359 -- spaghetti-monster/rampingios/Makefile | 7 - spaghetti-monster/rampingios/build-all.sh | 13 - spaghetti-monster/rampingios/rampingios-v3.html | 501 -- spaghetti-monster/rampingios/rampingios-v3.md | 262 -- spaghetti-monster/rampingios/rampingios-v3.txt | 324 -- spaghetti-monster/rampingios/rampingiosv3-ui.png | Bin 240749 -> 0 bytes spaghetti-monster/rampingios/rampingiosv3.c | 1253 ----- spaghetti-monster/rampingios/rampingiosv3.svg | 4113 ---------------- spaghetti-monster/spaghetti-monster.h | 75 - spaghetti-monster/spaghetti-monster.txt | 325 -- spaghetti-monster/werner/Makefile | 7 - spaghetti-monster/werner/README | 56 - spaghetti-monster/werner/build-all.sh | 15 - spaghetti-monster/werner/werner.c | 715 --- ui/anduril/Makefile | 14 + ui/anduril/anduril.c | 396 ++ ui/anduril/aux-leds.c | 210 + ui/anduril/aux-leds.h | 65 + ui/anduril/battcheck-mode-fsm.h | 18 + ui/anduril/battcheck-mode.c | 82 + ui/anduril/battcheck-mode.h | 12 + ui/anduril/beacon-mode.c | 53 + ui/anduril/beacon-mode.h | 9 + ui/anduril/candle-mode.c | 136 + ui/anduril/candle-mode.h | 13 + ui/anduril/channel-modes.c | 237 + ui/anduril/channel-modes.h | 26 + ui/anduril/config-default.h | 207 + ui/anduril/config-mode.c | 196 + ui/anduril/config-mode.h | 24 + ui/anduril/factory-reset-fsm.h | 10 + ui/anduril/factory-reset.c | 73 + ui/anduril/factory-reset.h | 8 + ui/anduril/ff-strobe-modes.c | 62 + ui/anduril/ff-strobe-modes.h | 15 + ui/anduril/load-save-config-fsm.h | 139 + ui/anduril/load-save-config.c | 33 + ui/anduril/load-save-config.h | 173 + ui/anduril/lockout-mode-fsm.h | 11 + ui/anduril/lockout-mode.c | 219 + ui/anduril/lockout-mode.h | 16 + ui/anduril/misc.c | 42 + ui/anduril/misc.h | 10 + ui/anduril/momentary-mode.c | 67 + ui/anduril/momentary-mode.h | 11 + ui/anduril/off-mode.c | 384 ++ ui/anduril/off-mode.h | 12 + ui/anduril/ramp-mode-fsm.h | 38 + ui/anduril/ramp-mode.c | 741 +++ ui/anduril/ramp-mode.h | 224 + ui/anduril/smooth-steps.c | 47 + ui/anduril/smooth-steps.h | 19 + ui/anduril/sos-mode.c | 56 + ui/anduril/sos-mode.h | 11 + ui/anduril/strobe-modes-fsm.h | 55 + ui/anduril/strobe-modes.c | 332 ++ ui/anduril/strobe-modes.h | 71 + ui/anduril/sunset-timer.c | 60 + ui/anduril/sunset-timer.h | 17 + ui/anduril/tactical-mode.c | 109 + ui/anduril/tactical-mode.h | 22 + ui/anduril/tempcheck-mode.c | 56 + ui/anduril/tempcheck-mode.h | 12 + ui/anduril/tint-ramping.c | 86 + ui/anduril/tint-ramping.h | 21 + ui/anduril/version-check-mode.c | 31 + ui/anduril/version-check-mode.h | 19 + ui/anduril/version.h | 4 + ui/baton/baton.c | 188 + ui/baton/baton.txt | 21 + ui/darkhorse/darkhorse.c | 367 ++ ui/fireflies-ui/Makefile | 7 + ui/fireflies-ui/build-all.sh | 13 + ui/fireflies-ui/cfg-ff-e01.h | 44 + ui/fireflies-ui/cfg-ff-e07-2.h | 27 + ui/fireflies-ui/fireflies-ui.c | 2386 ++++++++++ ui/meteor/meteor.c | 556 +++ ui/momentary/momentary.c | 80 + ui/ramping-ui/ramping-ui.c | 359 ++ ui/rampingios/Makefile | 7 + ui/rampingios/build-all.sh | 13 + ui/rampingios/rampingios-v3.html | 501 ++ ui/rampingios/rampingios-v3.md | 262 ++ ui/rampingios/rampingios-v3.txt | 324 ++ ui/rampingios/rampingiosv3-ui.png | Bin 0 -> 240749 bytes ui/rampingios/rampingiosv3.c | 1253 +++++ ui/rampingios/rampingiosv3.svg | 4113 ++++++++++++++++ ui/werner/Makefile | 7 + ui/werner/README | 56 + ui/werner/build-all.sh | 15 + ui/werner/werner.c | 715 +++ 505 files changed, 39413 insertions(+), 39866 deletions(-) create mode 100644 ChangeLog.md create mode 100644 MODELS create mode 100755 bin/build-all.sh create mode 100755 bin/models.py create mode 100755 bin/steps.py create mode 100644 docs/anduril-manual.txt create mode 100644 docs/anduril-ui.png create mode 100644 docs/anduril.svg create mode 100644 fsm/COPYING create mode 100644 fsm/adc.c create mode 100644 fsm/adc.h create mode 100644 fsm/chan-aux.c create mode 100644 fsm/chan-aux.h create mode 100644 fsm/chan-rgbaux.c create mode 100644 fsm/chan-rgbaux.h create mode 100644 fsm/channels.c create mode 100644 fsm/channels.h create mode 100644 fsm/eeprom.c create mode 100644 fsm/eeprom.h create mode 100644 fsm/events.c create mode 100644 fsm/events.h create mode 100644 fsm/main.c create mode 100644 fsm/main.h create mode 100644 fsm/misc.c create mode 100644 fsm/misc.h create mode 100644 fsm/pcint.c create mode 100644 fsm/pcint.h create mode 100644 fsm/ramping.c create mode 100644 fsm/ramping.h create mode 100644 fsm/random.c create mode 100644 fsm/random.h create mode 100644 fsm/spaghetti-monster.h create mode 100644 fsm/spaghetti-monster.txt create mode 100644 fsm/standby.c create mode 100644 fsm/standby.h create mode 100644 fsm/states.c create mode 100644 fsm/states.h create mode 100644 fsm/wdt.c create mode 100644 fsm/wdt.h create mode 100644 hw/BRANDS create mode 100644 hw/fireflies/e01/cfg.h create mode 100644 hw/fireflies/pl47-219/cfg.h create mode 100644 hw/fireflies/pl47/cfg.h create mode 100644 hw/fireflies/pl47/hwdef.h create mode 100644 hw/fireflies/pl47g2/cfg.h create mode 100644 hw/fireflies/rot66-219/cfg.h create mode 100644 hw/fireflies/rot66/cfg.h create mode 100644 hw/fireflies/rot66/hwdef.h create mode 100644 hw/fireflies/rot66g2/cfg.h create mode 100644 hw/gchart/fet1-t1616/cfg.h create mode 100644 hw/gchart/fet1-t1616/hwdef.h create mode 100644 hw/hank/emisar-2ch-fet/cfg.h create mode 100644 hw/hank/emisar-2ch-fet/hwdef.c create mode 100644 hw/hank/emisar-2ch-fet/hwdef.h create mode 100644 hw/hank/emisar-2ch/cfg.h create mode 100644 hw/hank/emisar-2ch/hwdef.c create mode 100644 hw/hank/emisar-2ch/hwdef.h create mode 100644 hw/hank/emisar-d1/cfg.h create mode 100644 hw/hank/emisar-d18-219/cfg.h create mode 100644 hw/hank/emisar-d18/cfg.h create mode 100644 hw/hank/emisar-d18/hwdef.h create mode 100644 hw/hank/emisar-d1s/cfg.h create mode 100644 hw/hank/emisar-d1v2-7135-fet/cfg.h create mode 100644 hw/hank/emisar-d1v2-linear-fet/cfg.h create mode 100644 hw/hank/emisar-d1v2-nofet/cfg.h create mode 100644 hw/hank/emisar-d4-219/cfg.h create mode 100644 hw/hank/emisar-d4/cfg.h create mode 100644 hw/hank/emisar-d4/hwdef.c create mode 100644 hw/hank/emisar-d4/hwdef.h create mode 100644 hw/hank/emisar-d4k-3ch/cfg.h create mode 100644 hw/hank/emisar-d4k-3ch/hwdef.c create mode 100644 hw/hank/emisar-d4k-3ch/hwdef.h create mode 100644 hw/hank/emisar-d4s-219/cfg.h create mode 100644 hw/hank/emisar-d4s/cfg.h create mode 100644 hw/hank/emisar-d4s/hwdef.h create mode 100644 hw/hank/emisar-d4sv2-219/cfg.h create mode 100644 hw/hank/emisar-d4sv2/cfg.h create mode 100644 hw/hank/emisar-d4sv2/hwdef.c create mode 100644 hw/hank/emisar-d4sv2/hwdef.h create mode 100644 hw/hank/emisar-d4v2-219/cfg.h create mode 100644 hw/hank/emisar-d4v2-nofet/cfg.h create mode 100644 hw/hank/emisar-d4v2-nofet/hwdef.c create mode 100644 hw/hank/emisar-d4v2/cfg.h create mode 100644 hw/hank/emisar-d4v2/hwdef.c create mode 100644 hw/hank/emisar-d4v2/hwdef.h create mode 100644 hw/hank/hank-cfg.h create mode 100644 hw/hank/noctigon-dm11-boost/cfg.h create mode 100644 hw/hank/noctigon-dm11-boost/hwdef.c create mode 100644 hw/hank/noctigon-dm11-boost/hwdef.h create mode 100644 hw/hank/noctigon-dm11-nofet/cfg.h create mode 100644 hw/hank/noctigon-dm11-sbt90/cfg.h create mode 100644 hw/hank/noctigon-dm11/cfg.h create mode 100644 hw/hank/noctigon-dm11/hwdef.h create mode 100644 hw/hank/noctigon-k1-boost/cfg.h create mode 100644 hw/hank/noctigon-k1-boost/hwdef.h create mode 100644 hw/hank/noctigon-k1-sbt90/cfg.h create mode 100644 hw/hank/noctigon-k1-sbt90/hwdef.h create mode 100644 hw/hank/noctigon-k1/cfg.h create mode 100644 hw/hank/noctigon-k1/hwdef.c create mode 100644 hw/hank/noctigon-k1/hwdef.h create mode 100644 hw/hank/noctigon-k9.3-219/cfg.h create mode 100644 hw/hank/noctigon-k9.3-nofet/cfg.h create mode 100644 hw/hank/noctigon-k9.3/cfg.h create mode 100644 hw/hank/noctigon-kr4-219/cfg.h create mode 100644 hw/hank/noctigon-kr4-219b/cfg.h create mode 100644 hw/hank/noctigon-kr4-2ch/cfg.h create mode 100644 hw/hank/noctigon-kr4-2ch/hwdef.h create mode 100644 hw/hank/noctigon-kr4-boost/cfg.h create mode 100644 hw/hank/noctigon-kr4-boost/hwdef.h create mode 100644 hw/hank/noctigon-kr4-nofet/cfg.h create mode 100644 hw/hank/noctigon-kr4-nofet/hwdef.c create mode 100644 hw/hank/noctigon-kr4/cfg.h create mode 100644 hw/hank/noctigon-kr4/hwdef.c create mode 100644 hw/hank/noctigon-kr4/hwdef.h create mode 100644 hw/hank/noctigon-m44/cfg.h create mode 100644 hw/hank/noctigon-m44/hwdef.c create mode 100644 hw/hank/noctigon-m44/hwdef.h create mode 100644 hw/lumintop/blf-gt-mini/cfg.h create mode 100644 hw/lumintop/blf-gt-mini/hwdef.h create mode 100644 hw/lumintop/blf-gt/cfg.h create mode 100644 hw/lumintop/blf-gt/hwdef.h create mode 100644 hw/lumintop/fw3a-219/cfg.h create mode 100644 hw/lumintop/fw3a-nofet/cfg.h create mode 100644 hw/lumintop/fw3a/cfg.h create mode 100644 hw/lumintop/fw3a/hwdef.c create mode 100644 hw/lumintop/fw3a/hwdef.h create mode 100644 hw/lumintop/fw3x-lume1/cfg.h create mode 100644 hw/lumintop/fw3x-lume1/hwdef.c create mode 100644 hw/lumintop/fw3x-lume1/hwdef.h create mode 100644 hw/mateminco/mf01-mini/cfg.h create mode 100644 hw/mateminco/mf01-mini/hwdef.h create mode 100644 hw/mateminco/mf01s/cfg.h create mode 100644 hw/mateminco/mf01s/hwdef.h create mode 100644 hw/mateminco/mt35-mini/cfg.h create mode 100644 hw/mateminco/mt35-mini/hwdef.h create mode 100644 hw/sofirn/blf-lt1-t1616/cfg.h create mode 100644 hw/sofirn/blf-lt1-t1616/hwdef.c create mode 100644 hw/sofirn/blf-lt1-t1616/hwdef.h create mode 100644 hw/sofirn/blf-lt1/cfg.h create mode 100644 hw/sofirn/blf-lt1/hwdef.c create mode 100644 hw/sofirn/blf-lt1/hwdef.h create mode 100644 hw/sofirn/blf-q8-t1616/cfg.h create mode 100644 hw/sofirn/blf-q8-t1616/hwdef.h create mode 100644 hw/sofirn/blf-q8/cfg.h create mode 100644 hw/sofirn/blf-q8/hwdef.h create mode 100644 hw/sofirn/lt1s-pro/cfg.h create mode 100644 hw/sofirn/lt1s-pro/hwdef.c create mode 100644 hw/sofirn/lt1s-pro/hwdef.h create mode 100644 hw/sofirn/sc21-pro/cfg.h create mode 100644 hw/sofirn/sp10-pro/cfg.h create mode 100644 hw/sofirn/sp10-pro/hwdef.c create mode 100644 hw/sofirn/sp10-pro/hwdef.h create mode 100644 hw/sofirn/sp36-t1616/cfg.h create mode 100644 hw/sofirn/sp36/cfg.h create mode 100644 hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/cfg.h create mode 100644 hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/hwdef.h create mode 100644 hw/thefreeman/boost21-mp3431-hdr-dac-argb/cfg.h create mode 100644 hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.c create mode 100644 hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.h create mode 100644 hw/thefreeman/lin16dac/cfg.h create mode 100644 hw/thefreeman/lin16dac/hwdef.c create mode 100644 hw/thefreeman/lin16dac/hwdef.h create mode 100644 hw/wurkkos/fc13/cfg.h create mode 100644 hw/wurkkos/ts10-rgbaux/cfg.h create mode 100644 hw/wurkkos/ts10/cfg.h create mode 100644 hw/wurkkos/ts10/hwdef.c create mode 100644 hw/wurkkos/ts10/hwdef.h create mode 100644 hw/wurkkos/ts11/cfg.h create mode 100644 hw/wurkkos/ts25/cfg.h create mode 100644 hw/wurkkos/ts25/hwdef.c create mode 100644 hw/wurkkos/ts25/hwdef.h create mode 100644 hw/wurkkos/wurkkos-cfg.h delete mode 100644 hwdef-FET_7135.h delete mode 100644 hwdef-Ferrero_Rocher.h delete mode 100644 hwdef-TK_Saber.h delete mode 100644 hwdef-Tripledown.h delete mode 100644 hwdef-blf-gt-mini.h delete mode 100644 hwdef-blf-gt.h delete mode 100644 hwdef-blf-lt1-t1616.c delete mode 100644 hwdef-blf-lt1-t1616.h delete mode 100644 hwdef-blf-lt1.c delete mode 100644 hwdef-blf-lt1.h delete mode 100644 hwdef-blf-q8-t1616.h delete mode 100644 hwdef-blf-q8.h delete mode 100644 hwdef-emisar-2ch-fet.c delete mode 100644 hwdef-emisar-2ch-fet.h delete mode 100644 hwdef-emisar-2ch.c delete mode 100644 hwdef-emisar-2ch.h delete mode 100644 hwdef-emisar-d18.h delete mode 100644 hwdef-emisar-d4.c delete mode 100644 hwdef-emisar-d4.h delete mode 100644 hwdef-emisar-d4k-3ch.c delete mode 100644 hwdef-emisar-d4k-3ch.h delete mode 100644 hwdef-emisar-d4s.h delete mode 100644 hwdef-emisar-d4sv2.c delete mode 100644 hwdef-emisar-d4sv2.h delete mode 100644 hwdef-emisar-d4v2-nofet.c delete mode 100644 hwdef-emisar-d4v2.c delete mode 100644 hwdef-emisar-d4v2.h delete mode 100644 hwdef-ff-pl47.h delete mode 100644 hwdef-ff-rot66.h delete mode 100644 hwdef-fw3a.c delete mode 100644 hwdef-fw3a.h delete mode 100644 hwdef-fw3x-lume1.c delete mode 100644 hwdef-fw3x-lume1.h delete mode 100644 hwdef-gchart-fet1-t1616.h delete mode 100644 hwdef-mateminco-mf01-mini.h delete mode 100644 hwdef-mateminco-mf01s.h delete mode 100644 hwdef-mateminco-mt35-mini.h delete mode 100644 hwdef-nanjg.h delete mode 100644 hwdef-noctigon-dm11-boost.c delete mode 100644 hwdef-noctigon-dm11-boost.h delete mode 100644 hwdef-noctigon-dm11.h delete mode 100644 hwdef-noctigon-k1-boost.h delete mode 100644 hwdef-noctigon-k1-sbt90.h delete mode 100644 hwdef-noctigon-k1.c delete mode 100644 hwdef-noctigon-k1.h delete mode 100644 hwdef-noctigon-kr4-2ch.h delete mode 100644 hwdef-noctigon-kr4-boost.h delete mode 100644 hwdef-noctigon-kr4-nofet.c delete mode 100644 hwdef-noctigon-kr4.c delete mode 100644 hwdef-noctigon-kr4.h delete mode 100644 hwdef-noctigon-m44.c delete mode 100644 hwdef-noctigon-m44.h delete mode 100644 hwdef-sofirn-lt1s-pro.c delete mode 100644 hwdef-sofirn-lt1s-pro.h delete mode 100644 hwdef-sofirn-sp10-pro.c delete mode 100644 hwdef-sofirn-sp10-pro.h delete mode 100755 hwdef-thefreeman-boost-fwaa-mp3432-hdr-dac-rgb.h delete mode 100644 hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.c delete mode 100644 hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.h delete mode 100644 hwdef-thefreeman-lin16dac.c delete mode 100644 hwdef-thefreeman-lin16dac.h delete mode 100644 hwdef-wurkkos-ts10.c delete mode 100644 hwdef-wurkkos-ts10.h delete mode 100644 hwdef-wurkkos-ts25.c delete mode 100644 hwdef-wurkkos-ts25.h delete mode 100644 spaghetti-monster/COPYING delete mode 100644 spaghetti-monster/anduril/BRANDS delete mode 100644 spaghetti-monster/anduril/ChangeLog.md delete mode 100644 spaghetti-monster/anduril/MODELS delete mode 100644 spaghetti-monster/anduril/Makefile delete mode 100644 spaghetti-monster/anduril/anduril-manual.txt delete mode 100644 spaghetti-monster/anduril/anduril-ui.png delete mode 100644 spaghetti-monster/anduril/anduril.c delete mode 100644 spaghetti-monster/anduril/anduril.svg delete mode 100644 spaghetti-monster/anduril/anduril.txt delete mode 100644 spaghetti-monster/anduril/aux-leds.c delete mode 100644 spaghetti-monster/anduril/aux-leds.h delete mode 100644 spaghetti-monster/anduril/battcheck-mode-fsm.h delete mode 100644 spaghetti-monster/anduril/battcheck-mode.c delete mode 100644 spaghetti-monster/anduril/battcheck-mode.h delete mode 100644 spaghetti-monster/anduril/beacon-mode.c delete mode 100644 spaghetti-monster/anduril/beacon-mode.h delete mode 100755 spaghetti-monster/anduril/build-all.sh delete mode 100644 spaghetti-monster/anduril/candle-mode.c delete mode 100644 spaghetti-monster/anduril/candle-mode.h delete mode 100644 spaghetti-monster/anduril/cfg-blf-gt-mini.h delete mode 100644 spaghetti-monster/anduril/cfg-blf-gt.h delete mode 100644 spaghetti-monster/anduril/cfg-blf-lantern-t1616.h delete mode 100644 spaghetti-monster/anduril/cfg-blf-lantern.h delete mode 100644 spaghetti-monster/anduril/cfg-blf-q8-t1616.h delete mode 100644 spaghetti-monster/anduril/cfg-blf-q8.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-2ch-fet.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-2ch.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-d1.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-d18-219.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-d18.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-d1s.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-d1v2-7135-fet.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-d1v2-linear-fet.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-d1v2-nofet.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-d4-219c.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-d4.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-d4k-3ch.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-d4s-219c.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-d4s.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-d4sv2-219.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-d4sv2.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-d4v2-219.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h delete mode 100644 spaghetti-monster/anduril/cfg-emisar-d4v2.h delete mode 100644 spaghetti-monster/anduril/cfg-ff-e01.h delete mode 100644 spaghetti-monster/anduril/cfg-ff-pl47-219.h delete mode 100644 spaghetti-monster/anduril/cfg-ff-pl47.h delete mode 100644 spaghetti-monster/anduril/cfg-ff-pl47g2.h delete mode 100644 spaghetti-monster/anduril/cfg-ff-rot66-219.h delete mode 100644 spaghetti-monster/anduril/cfg-ff-rot66.h delete mode 100644 spaghetti-monster/anduril/cfg-ff-rot66g2.h delete mode 100644 spaghetti-monster/anduril/cfg-fw3a-219.h delete mode 100644 spaghetti-monster/anduril/cfg-fw3a-nofet.h delete mode 100644 spaghetti-monster/anduril/cfg-fw3a.h delete mode 100644 spaghetti-monster/anduril/cfg-fw3x-lume1.h delete mode 100644 spaghetti-monster/anduril/cfg-gchart-fet1-t1616.h delete mode 100644 spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h delete mode 100644 spaghetti-monster/anduril/cfg-mateminco-mf01s.h delete mode 100644 spaghetti-monster/anduril/cfg-mateminco-mt35-mini.h delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-dm11-boost.h delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-dm11-nofet.h delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-dm11-sbt90.h delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-dm11.h delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-k1-boost.h delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-k1-sbt90.h delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-k1.h delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-k9.3-219.h delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-k9.3-nofet.h delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-k9.3.c delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-k9.3.h delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-kr4-219.h delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-kr4-219b.h delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-kr4-2ch.h delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-kr4-boost.h delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-kr4-nofet.h delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-kr4.h delete mode 100644 spaghetti-monster/anduril/cfg-noctigon-m44.h delete mode 100644 spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h delete mode 100644 spaghetti-monster/anduril/cfg-sofirn-sc21-pro.h delete mode 100644 spaghetti-monster/anduril/cfg-sofirn-sp10-pro.h delete mode 100644 spaghetti-monster/anduril/cfg-sofirn-sp36-t1616.h delete mode 100644 spaghetti-monster/anduril/cfg-sofirn-sp36.h delete mode 100755 spaghetti-monster/anduril/cfg-thefreeman-boost-fwaa.h delete mode 100644 spaghetti-monster/anduril/cfg-thefreeman-boost21-6a.h delete mode 100644 spaghetti-monster/anduril/cfg-thefreeman-lin16dac.h delete mode 100644 spaghetti-monster/anduril/cfg-wurkkos-fc13.h delete mode 100644 spaghetti-monster/anduril/cfg-wurkkos-ts10-rgbaux.h delete mode 100644 spaghetti-monster/anduril/cfg-wurkkos-ts10.h delete mode 100644 spaghetti-monster/anduril/cfg-wurkkos-ts11.h delete mode 100644 spaghetti-monster/anduril/cfg-wurkkos-ts25.h delete mode 100644 spaghetti-monster/anduril/channel-modes.c delete mode 100644 spaghetti-monster/anduril/channel-modes.h delete mode 100644 spaghetti-monster/anduril/config-default.h delete mode 100644 spaghetti-monster/anduril/config-mode.c delete mode 100644 spaghetti-monster/anduril/config-mode.h delete mode 100644 spaghetti-monster/anduril/factory-reset-fsm.h delete mode 100644 spaghetti-monster/anduril/factory-reset.c delete mode 100644 spaghetti-monster/anduril/factory-reset.h delete mode 100644 spaghetti-monster/anduril/ff-strobe-modes.c delete mode 100644 spaghetti-monster/anduril/ff-strobe-modes.h delete mode 100644 spaghetti-monster/anduril/hank-cfg.h delete mode 100644 spaghetti-monster/anduril/load-save-config-fsm.h delete mode 100644 spaghetti-monster/anduril/load-save-config.c delete mode 100644 spaghetti-monster/anduril/load-save-config.h delete mode 100644 spaghetti-monster/anduril/lockout-mode-fsm.h delete mode 100644 spaghetti-monster/anduril/lockout-mode.c delete mode 100644 spaghetti-monster/anduril/lockout-mode.h delete mode 100644 spaghetti-monster/anduril/misc.c delete mode 100644 spaghetti-monster/anduril/misc.h delete mode 100755 spaghetti-monster/anduril/models.py delete mode 100644 spaghetti-monster/anduril/momentary-mode.c delete mode 100644 spaghetti-monster/anduril/momentary-mode.h delete mode 100644 spaghetti-monster/anduril/off-mode.c delete mode 100644 spaghetti-monster/anduril/off-mode.h delete mode 100644 spaghetti-monster/anduril/ramp-mode-fsm.h delete mode 100644 spaghetti-monster/anduril/ramp-mode.c delete mode 100644 spaghetti-monster/anduril/ramp-mode.h delete mode 100644 spaghetti-monster/anduril/smooth-steps.c delete mode 100644 spaghetti-monster/anduril/smooth-steps.h delete mode 100644 spaghetti-monster/anduril/sos-mode.c delete mode 100644 spaghetti-monster/anduril/sos-mode.h delete mode 100755 spaghetti-monster/anduril/steps.py delete mode 100644 spaghetti-monster/anduril/strobe-modes-fsm.h delete mode 100644 spaghetti-monster/anduril/strobe-modes.c delete mode 100644 spaghetti-monster/anduril/strobe-modes.h delete mode 100644 spaghetti-monster/anduril/sunset-timer.c delete mode 100644 spaghetti-monster/anduril/sunset-timer.h delete mode 100644 spaghetti-monster/anduril/tactical-mode.c delete mode 100644 spaghetti-monster/anduril/tactical-mode.h delete mode 100644 spaghetti-monster/anduril/tempcheck-mode.c delete mode 100644 spaghetti-monster/anduril/tempcheck-mode.h delete mode 100644 spaghetti-monster/anduril/tint-ramping.c delete mode 100644 spaghetti-monster/anduril/tint-ramping.h delete mode 100644 spaghetti-monster/anduril/version-check-mode.c delete mode 100644 spaghetti-monster/anduril/version-check-mode.h delete mode 100644 spaghetti-monster/anduril/version.h delete mode 100644 spaghetti-monster/anduril/wurkkos-cfg.h delete mode 100644 spaghetti-monster/baton/baton.c delete mode 100644 spaghetti-monster/baton/baton.txt delete mode 100644 spaghetti-monster/chan-aux.c delete mode 100644 spaghetti-monster/chan-aux.h delete mode 100644 spaghetti-monster/chan-rgbaux.c delete mode 100644 spaghetti-monster/chan-rgbaux.h delete mode 100644 spaghetti-monster/darkhorse/darkhorse.c delete mode 100644 spaghetti-monster/fireflies-ui/Makefile delete mode 100755 spaghetti-monster/fireflies-ui/build-all.sh delete mode 100644 spaghetti-monster/fireflies-ui/cfg-ff-e01.h delete mode 100644 spaghetti-monster/fireflies-ui/cfg-ff-e07-2.h delete mode 100644 spaghetti-monster/fireflies-ui/fireflies-ui.c delete mode 100644 spaghetti-monster/fsm-adc.c delete mode 100644 spaghetti-monster/fsm-adc.h delete mode 100644 spaghetti-monster/fsm-channels.c delete mode 100644 spaghetti-monster/fsm-channels.h delete mode 100644 spaghetti-monster/fsm-eeprom.c delete mode 100644 spaghetti-monster/fsm-eeprom.h delete mode 100644 spaghetti-monster/fsm-events.c delete mode 100644 spaghetti-monster/fsm-events.h delete mode 100644 spaghetti-monster/fsm-main.c delete mode 100644 spaghetti-monster/fsm-main.h delete mode 100644 spaghetti-monster/fsm-misc.c delete mode 100644 spaghetti-monster/fsm-misc.h delete mode 100644 spaghetti-monster/fsm-pcint.c delete mode 100644 spaghetti-monster/fsm-pcint.h delete mode 100644 spaghetti-monster/fsm-ramping.c delete mode 100644 spaghetti-monster/fsm-ramping.h delete mode 100644 spaghetti-monster/fsm-random.c delete mode 100644 spaghetti-monster/fsm-random.h delete mode 100644 spaghetti-monster/fsm-standby.c delete mode 100644 spaghetti-monster/fsm-standby.h delete mode 100644 spaghetti-monster/fsm-states.c delete mode 100644 spaghetti-monster/fsm-states.h delete mode 100644 spaghetti-monster/fsm-wdt.c delete mode 100644 spaghetti-monster/fsm-wdt.h delete mode 100644 spaghetti-monster/meteor/meteor.c delete mode 100644 spaghetti-monster/momentary/momentary.c delete mode 100644 spaghetti-monster/ramping-ui/ramping-ui.c delete mode 100644 spaghetti-monster/rampingios/Makefile delete mode 100755 spaghetti-monster/rampingios/build-all.sh delete mode 100644 spaghetti-monster/rampingios/rampingios-v3.html delete mode 100644 spaghetti-monster/rampingios/rampingios-v3.md delete mode 100644 spaghetti-monster/rampingios/rampingios-v3.txt delete mode 100644 spaghetti-monster/rampingios/rampingiosv3-ui.png delete mode 100644 spaghetti-monster/rampingios/rampingiosv3.c delete mode 100644 spaghetti-monster/rampingios/rampingiosv3.svg delete mode 100644 spaghetti-monster/spaghetti-monster.h delete mode 100644 spaghetti-monster/spaghetti-monster.txt delete mode 100644 spaghetti-monster/werner/Makefile delete mode 100644 spaghetti-monster/werner/README delete mode 100755 spaghetti-monster/werner/build-all.sh delete mode 100644 spaghetti-monster/werner/werner.c create mode 100644 ui/anduril/Makefile create mode 100644 ui/anduril/anduril.c create mode 100644 ui/anduril/aux-leds.c create mode 100644 ui/anduril/aux-leds.h create mode 100644 ui/anduril/battcheck-mode-fsm.h create mode 100644 ui/anduril/battcheck-mode.c create mode 100644 ui/anduril/battcheck-mode.h create mode 100644 ui/anduril/beacon-mode.c create mode 100644 ui/anduril/beacon-mode.h create mode 100644 ui/anduril/candle-mode.c create mode 100644 ui/anduril/candle-mode.h create mode 100644 ui/anduril/channel-modes.c create mode 100644 ui/anduril/channel-modes.h create mode 100644 ui/anduril/config-default.h create mode 100644 ui/anduril/config-mode.c create mode 100644 ui/anduril/config-mode.h create mode 100644 ui/anduril/factory-reset-fsm.h create mode 100644 ui/anduril/factory-reset.c create mode 100644 ui/anduril/factory-reset.h create mode 100644 ui/anduril/ff-strobe-modes.c create mode 100644 ui/anduril/ff-strobe-modes.h create mode 100644 ui/anduril/load-save-config-fsm.h create mode 100644 ui/anduril/load-save-config.c create mode 100644 ui/anduril/load-save-config.h create mode 100644 ui/anduril/lockout-mode-fsm.h create mode 100644 ui/anduril/lockout-mode.c create mode 100644 ui/anduril/lockout-mode.h create mode 100644 ui/anduril/misc.c create mode 100644 ui/anduril/misc.h create mode 100644 ui/anduril/momentary-mode.c create mode 100644 ui/anduril/momentary-mode.h create mode 100644 ui/anduril/off-mode.c create mode 100644 ui/anduril/off-mode.h create mode 100644 ui/anduril/ramp-mode-fsm.h create mode 100644 ui/anduril/ramp-mode.c create mode 100644 ui/anduril/ramp-mode.h create mode 100644 ui/anduril/smooth-steps.c create mode 100644 ui/anduril/smooth-steps.h create mode 100644 ui/anduril/sos-mode.c create mode 100644 ui/anduril/sos-mode.h create mode 100644 ui/anduril/strobe-modes-fsm.h create mode 100644 ui/anduril/strobe-modes.c create mode 100644 ui/anduril/strobe-modes.h create mode 100644 ui/anduril/sunset-timer.c create mode 100644 ui/anduril/sunset-timer.h create mode 100644 ui/anduril/tactical-mode.c create mode 100644 ui/anduril/tactical-mode.h create mode 100644 ui/anduril/tempcheck-mode.c create mode 100644 ui/anduril/tempcheck-mode.h create mode 100644 ui/anduril/tint-ramping.c create mode 100644 ui/anduril/tint-ramping.h create mode 100644 ui/anduril/version-check-mode.c create mode 100644 ui/anduril/version-check-mode.h create mode 100644 ui/anduril/version.h create mode 100644 ui/baton/baton.c create mode 100644 ui/baton/baton.txt create mode 100644 ui/darkhorse/darkhorse.c create mode 100644 ui/fireflies-ui/Makefile create mode 100755 ui/fireflies-ui/build-all.sh create mode 100644 ui/fireflies-ui/cfg-ff-e01.h create mode 100644 ui/fireflies-ui/cfg-ff-e07-2.h create mode 100644 ui/fireflies-ui/fireflies-ui.c create mode 100644 ui/meteor/meteor.c create mode 100644 ui/momentary/momentary.c create mode 100644 ui/ramping-ui/ramping-ui.c create mode 100644 ui/rampingios/Makefile create mode 100755 ui/rampingios/build-all.sh create mode 100644 ui/rampingios/rampingios-v3.html create mode 100644 ui/rampingios/rampingios-v3.md create mode 100644 ui/rampingios/rampingios-v3.txt create mode 100644 ui/rampingios/rampingiosv3-ui.png create mode 100644 ui/rampingios/rampingiosv3.c create mode 100644 ui/rampingios/rampingiosv3.svg create mode 100644 ui/werner/Makefile create mode 100644 ui/werner/README create mode 100755 ui/werner/build-all.sh create mode 100644 ui/werner/werner.c diff --git a/ChangeLog.md b/ChangeLog.md new file mode 100644 index 0000000..0aaa392 --- /dev/null +++ b/ChangeLog.md @@ -0,0 +1,245 @@ +# ChangeLog + +Items should be sorted roughly in order of most to least important, with +model-specific changes at the bottom. Most items should use one of these +formats: + + - Breaking change: + - Added + - Fixed + - Removed + - Changed + - Documented + - @brand-model: Hardware-specific change (NNNN, NNNN, ...) + +# Next + +# 2023-10-31 + +General: + +- Finally finished converting all lights to the multi-channel API. + (but a few are untested due to hardware being unavailable) +- Enabled smooth steps on almost all supported lights, including old ones. + (in some cases, on old t85 lights, some other things may have been removed + to make room) +- Made smooth steps work better, and fixed several bugs. +- Added/fixed RGB aux voltage on dual-fuel AA/li-ion drivers. +- Fixed delay timing on all supported MCUs, so 1 "second" is closer to an + actual second (like in beacon mode or biking mode). + (especially attiny1616, which was 17% too slow) +- Raised default thermal limit to 50C on attiny1616-based lights, since + it doesn't need as big an error margin as older MCUs. +- Fixed missing item in globals menu on some lights. +- Misc internal improvements and per-light tweaks. +- Added a ChangeLog.md, to periodically summarize changes in a format which is + more readable than the raw commit logs. + +New lights: + +- @thefreeman-boost21-6a: Added. (1631) + (very nice HDR boost driver which fits into a FW3A) +- @thefreeman-boost-fwaa: Added. (1632) + (very nice AA/li-ion HDR boost driver which fits into a FWAA) + +Hardware-specific changes: + +- Upgraded several builds to use delta-sigma modulation (DSM), for + lower lows and smoother ramping with less flicker or ripple: + - @blf-lt1 (0621) + - @blf-lt1-t1616 (0622) + - @emisar-d4k-3ch (0151) + (**dramatically** improves resolution and low modes on its 8-bit channel) + - @noctigon-dm11-boost (0273) + - @noctigon-kr4-boost (0216) + - @noctigon-k1-boost (0253) + - @noctigon-m44 (0143) + +- Upgraded some builds to use dynamic PWM, for lower lows and smoother ramping: + - @blf-q8-t1616, @sofirn-sp36-t1616 (0613, 0614) + - @gchart-fet1-t1616 (1618) + - @noctigon-k1-sbt90 (0252) + +- @wurkkos-ts10, @wurkkos-ts10-rgbaux: Fixed too-high default ceiling. (0713, 0714) + +# 2023-10-01 + +General: + +- Updated a bunch of lights to work with the new multi-channel API. +- Added "smooth steps" a.k.a. "soft start", to make on/off and step changes + smooth. (enabled by default, can be turned off in Ramp Extras menu) +- User can save a different channel mode for each strobe-group mode. +- Made strobes/blinkies more responsive in Tactical Mode. +- Fixed bug: Tactical Mode clobbers strobe group memory. +- Fixed some minor post-off voltage display bugs. +- Made RGB button brightness update faster in blinky modes. +- Fixed bug: Wrong channel after colored factory reset. +- @attiny1616, @attiny1634: Partially fixed oscillating aux LED voltage + colors while off. Better than before, but can still happen in some cases. +- @attiny1616: Fixed spurious voltage warnings in sleep mode. (it could + sometimes go from Lockout mode to Off mode by itself) +- Lots of internal refactoring. + +New lights: + +- @emisar-2ch-fet: Added. (0136) +- @emisar-d4k-3ch: Added. (0151) +- @noctigon-m44: Added. (0143) +- @wurkkos-ts10-rgbaux: Added. (0713) + +Hardware-specific changes: + +- @ff-e01, @ff-pl47, @ff-pl47g2: Enabled smooth steps instead of SOS mode. + (0421, 0422, 0423, 0441) +- @emisar-2ch, @noctigon-m44: Added RGB aux channel modes. (0135, 0143) +- @emisar-2ch-fet, @noctigon-k9.3: New ramps with better-calibrated shape. + (0136, 0261) +- @emisar-d4v2-nofet: New ramp table. (0115) +- @emisar-d4sv2-tintramp: Removed / renamed. (0135, 0136) +- @noctigon-k9.3: Fixed years-old mess. Merged builds, converted to + multi-channel, removed old builds, generally got K9.3 working quite a bit + better. (0261, 0262, 0263, 0265, 0266, 0267) +- @noctigon-m44: Lower moon, and greatly reduced flicker. (0143) +- @sofirn-lt1s-pro: Allow configuring the blink channel. (0623) +- @wurkkos: Raised default temperature limit to 50 C. (07xx) +- @wurkkos-ts10: Better / smoother ramp. (0713, 0714) + +# 2023-06-29 + +- Fixed red aux blink on 1st frame of post-off voltage display +- Removed Harry Potter references because its author (J.K. Rowling) spreads + hate +- @noctigon-kr4: Fixed thermal regulation (0211, 0212, 0213, 0214, 0215, 0216) + +# 2023-05-30 + +- Breaking change: Changed version check from YYYYMMDDXXXX to XXXX.YYYY-MM-DD, + where the punctuation makes a "buzz" instead of number blinks. +- Fixed issue where some lights always thought they were overheating (when + cold, or after a factory reset) +- Fixed aux pre-flash +- Fixed a rare corner case where gradual brightness adjustments stopped at 99% + power instead of 100% power while going up from level 149/150 to 150/150 on + some lights +- Changed "0" digit readout to use the same timing as other short blinks, to + help ensure it works on all lights. +- Minor code changes with no runtime impact +- Documented new version check format +- @wurkkos: Added red+blue police strobe (0715, 0716, 0717) +- @noctigon-kr4: Broke thermal regulation (oops) (0211, 0212, 0213, 0214, + 0215, 0216) +- @noctigon-kr4: Use 7 aux channel modes instead of 3 (0211, 0212, 0213, 0214, + 0215, 0216) +- @emisar-d4v2: Changed number blinks from aux to main LEDs by default (0113, + 0114, 0115, 0123) + +# 2023-05-17 + +- @noctigon-dm11-12v: Renamed to noctigon-dm11-boost (0273) +- @noctigon-dm11-boost: Now supported in multi-channel branch (0273) + +# 2023-05-02 + +- Added ability to set channel mode for number readouts (batt check, temp + check, version check). Press 3C in batt check mode to change the blink + channel. +- Changed post-off voltage display: + - Display for 4s by default, not 5s + - Made timing user-configurable in battcheck 7H menu item 2 + (1 click per second the display should last) + - Use low brightness when torch was at moon level before + - Skip the voltage display after UI actions which didn't change the mode + (like "Off -> 7C" to change aux LED settings) +- Changed RGB button behavior: While torch is on, use low mode below a + configured level for real-time voltage color display, instead of always + using high mode +- Fixed bug: Channel change could stick when activating a config menu from + battcheck (the blink function changed the channel, then the config menu + saved it, then the blink function restored it, then the config menu restored + it to the value it saved, which was wrong) +- Documented ramp 6C, ramp 4C, ramp 4H, lockout 3H, battcheck 3C, and post-off + voltage display config +- @emisar-d4v2: Added the rest of the aux RGB colors as channel modes, and set + aux "white" as the mode it uses to blink out numbers (0113, 0114, 0115, + 0123) +- @wurkkos-ts10: Converted to multi-channel, and gave it a new ramp with + better low modes (0714) +- @wurkkos-ts25: Converted to multi-channel, and gave it a smoother ramp + (0715) +- @wurkkos: Added Wurkkos FC13 and TS11 (0716, 0717) + +# 2023-04-29 + +- Changed lockout mode: + - Fixed manual memory timer and tint + - Added 3H for next channel (to change channels without unlocking, was + possible before but needed to be updated for the new channel system) + - Made 3H+ use mem level instead of lowest moon (this is needed for + making the channel discernible, and also helps make aux LED controls + stand out more) +- @emisar, @noctigon: Added Extended Simple UI to Hank's config, so a few more + features are allowed in simple mode +- @emisar-d4v2, @noctigon-kr4: Slightly smaller ROM size +- @emisar-d4sv2: Converted to multi-channel, and updated it to use dynamic PWM + for a smoother ramp with lower lows (0133, 0134) +- @noctigon-kr4: Converted to multi-channel (0211, 0212, 0213, 0214) +- @noctigon-kr4: Don't blink at top of regulated power (0211, 0213, 0214) + +# 2023-04-28 + +- Added voltage display (by color) on RGB aux LEDs after turning the main LEDs + off +- Changed RGB aux: Always preview in high mode while changing color or pattern +- Changed default RGB aux color in lockout mode: Uses blinking voltage, + instead of blinking disco (unless model-specific config defines a different + default) +- Changed tactical mode default config: only use 2-color strobe if it's on + main LEDs, not aux LEDs +- @emisar-d4v2: Smoother ramp (0113, 0114) +- @emisar-d4v2: Added hidden channel modes for RGB aux LEDs (0113, 0114, 0115) + +# 2023-04-27 + +- Changed channel mode menu to preview channels during configuration +- Changed tactical mode default config: use 2-color police strobe if it + exists, instead of single-color strobe +- Fixed unnecessary flickering when changing channel modes from/to the same + value +- Fixed sleep voltage measurement on attiny1616 +- @noctigon-kr4-tintramp: Converted to multi-channel, renamed to + noctigon-kr4-2ch (0215) + +# 2023-04-25 + +- Fixed old old bug(s) with voltage measurement while asleep, while also + adding other improvements: + - Fixed oscillating RGB aux colors while asleep in voltage mode, mostly. + Some much smaller oscillations are still possible, but the primary issue + has been fixed, I think. + - Fixed old old bug which could cause spurious low-voltage warnings while + asleep (previously required a messy code workaround). + - Reduced avg standby power by ~15uA. + - Now measures voltage every 1s instead of 8s while asleep. + - Changed RGB aux behavior: Puts aux LEDs in high mode for 3 seconds after + light goes to sleep (much easier to see the voltage this way) + (this change was obsoleted soon by a better post-off voltage display) + - Broke sleep voltage measurement on attiny1616 (oops) +- Changed internal details for how gradual ramping works +- @emisar-d4sv2-tintramp: Converted to multi-channel, renamed to emisar-2ch. + (0135) +- @sofirn-lt1s-pro: Updated to use today's new code internals (0623) + +# 2023-04-19 + +- Added stepped tint ramping +- Documented new channel modes system +- @sofirn-lt1s-pro: Added white-only auto-tint mode (0623) + +# Older: TODO + +- Need to import old commit logs as changelog items + + + + diff --git a/MODELS b/MODELS new file mode 100644 index 0000000..f4e9fdf --- /dev/null +++ b/MODELS @@ -0,0 +1,85 @@ +Model Name MCU +----- ---- --- +0111 emisar-d4 attiny85 +0112 emisar-d4-219c attiny85 +0113 emisar-d4v2 attiny1634 +0114 emisar-d4v2-219 attiny1634 +0115 emisar-d4v2-nofet attiny1634 +0121 emisar-d1 attiny85 +0122 emisar-d1s attiny85 +0123 emisar-d1v2-7135-fet attiny1634 +0124 emisar-d1v2-linear-fet attiny1634 +0125 emisar-d1v2-nofet attiny1634 +0131 emisar-d4s attiny85 +0132 emisar-d4s-219c attiny85 +0133 emisar-d4sv2 attiny1634 +0134 emisar-d4sv2-219 attiny1634 +0135 emisar-2ch attiny1634 +0135 emisar-d4sv2-tintramp attiny1634 (old) +0136 emisar-2ch-fet attiny1634 +0136 emisar-d4sv2-tintramp-fet attiny1634 (old) +0141 emisar-d18 attiny85 +0142 emisar-d18-219 attiny85 +0143 noctigon-m44 attiny1634 +0151 emisar-d4k-3ch attiny1634 +0211 noctigon-kr4 attiny1634 +0212 noctigon-kr4-nofet attiny1634 +0213 noctigon-kr4-219 attiny1634 +0214 noctigon-kr4-219b attiny1634 +0215 noctigon-kr4-2ch attiny1634 +0215 noctigon-kr4-tintramp attiny1634 (old) +0216 noctigon-kr4-boost attiny1634 +0216 noctigon-kr4-12v attiny1634 (old) +0251 noctigon-k1 attiny1634 +0252 noctigon-k1-sbt90 attiny1634 +0253 noctigon-k1-boost attiny1634 +0253 noctigon-k1-12v attiny1634 (old) +0261 noctigon-k9.3 attiny1634 +0262 noctigon-k9.3-nofet attiny1634 +0263 noctigon-k9.3-219 attiny1634 +0265 noctigon-k9.3-tintramp-nofet attiny1634 (old, use 0262) +0266 noctigon-k9.3-tintramp-fet attiny1634 (old, use 0261) +0267 noctigon-k9.3-tintramp-219 attiny1634 (old, use 0263) +0271 noctigon-dm11 attiny1634 +0272 noctigon-dm11-nofet attiny1634 +0273 noctigon-dm11-boost attiny1634 +0273 noctigon-dm11-12v attiny1634 (old) +0274 noctigon-dm11-sbt90 attiny1634 +0311 fw3a attiny85 +0312 fw3a-219 attiny85 +0313 fw3a-nofet attiny85 +0314 fw3x-lume1 attiny1634 +0321 blf-gt attiny85 +0322 blf-gt-mini attiny85 +0411 ff-rot66 attiny85 +0412 ff-rot66-219 attiny85 +0413 ff-rot66g2 attiny85 +0421 ff-pl47 attiny85 +0422 ff-pl47-219 attiny85 +0423 ff-pl47g2 attiny85 +0441 ff-e01 attiny85 +0511 mateminco-mf01s attiny85 +0521 mateminco-mf01-mini attiny85 +0531 mateminco-mt35-mini attiny85 +0611 blf-q8 attiny85 +0612 sofirn-sp36 attiny85 +0613 blf-q8-t1616 attiny1616 +0614 sofirn-sp36-t1616 attiny1616 +0621 blf-lantern attiny85 +0622 blf-lantern-t1616 attiny1616 +0623 sofirn-lt1s-pro attiny1616 +0631 sofirn-sp10-pro attiny1616 +0632 sofirn-sc21-pro attiny1616 +0713 wurkkos-ts10-rgbaux attiny1616 +0714 wurkkos-ts10 attiny1616 +0715 wurkkos-ts25 attiny1616 +0716 wurkkos-fc13 attiny1616 +0717 wurkkos-ts11 attiny1616 +1618 gchart-fet1-t1616 attiny1616 +1630 thefreeman-lin16dac attiny1616 +1631 thefreeman-boost21-6a attiny1616 +1632 thefreeman-boost-fwaa attiny1616 + +Duplicates: + +Missing: diff --git a/bin/build-all.sh b/bin/build-all.sh new file mode 100755 index 0000000..b3fc5d3 --- /dev/null +++ b/bin/build-all.sh @@ -0,0 +1,57 @@ +#!/bin/sh + +# Usage: build-all.sh [pattern] +# If pattern given, only build targets which match. + +if [ ! -z "$1" ]; then + SEARCH="$1" +fi + +UI=anduril + +date '+#define VERSION_NUMBER "%Y-%m-%d"' > version.h + +PASS=0 +FAIL=0 +PASSED='' +FAILED='' + +for TARGET in cfg-*.h ; do + + # maybe limit builds to a specific pattern + if [ ! -z "$SEARCH" ]; then + echo "$TARGET" | grep -i "$SEARCH" > /dev/null + if [ 0 != $? ]; then continue ; fi + fi + + # friendly name for this build + NAME=$(echo "$TARGET" | perl -ne '/cfg-(.*).h/ && print "$1\n";') + echo "===== $NAME =====" + + # figure out MCU type + ATTINY=$(grep 'ATTINY:' $TARGET | awk '{ print $3 }') + if [ -z "$ATTINY" ]; then ATTINY=85 ; fi + + # try to compile + echo ../../../bin/build.sh $ATTINY "$UI" "-DCFG_H=${TARGET}" + ../../../bin/build.sh $ATTINY "$UI" "-DCFG_H=${TARGET}" + + # track result, and rename compiled files + if [ 0 = $? ] ; then + mv -f "$UI".hex "$UI".$NAME.hex + PASS=$(($PASS + 1)) + PASSED="$PASSED $NAME" + else + echo "ERROR: build failed" + FAIL=$(($FAIL + 1)) + FAILED="$FAILED $NAME" + fi + +done + +# summary +echo "===== $PASS builds succeeded, $FAIL failed =====" +#echo "PASS: $PASSED" +if [ 0 != $FAIL ]; then + echo "FAIL:$FAILED" +fi diff --git a/bin/models.py b/bin/models.py new file mode 100755 index 0000000..1985352 --- /dev/null +++ b/bin/models.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python + +import os +import re + +def main(args): + """models.py: scan build targets to generate the MODELS file + """ + + models = [] + + # load all cfg-*.h files + paths = os.listdir('.') + for p in paths: + if p.startswith('cfg-') and p.endswith('.h'): + m = load_cfg(p) + models.append(m) + + # sort by model number + foo = [(m.num, m.name, m) for m in models] + foo.sort() + models = [x[-1] for x in foo] + + fmt = '%s\t%-30s\t%s' + print(fmt % ('Model', 'Name', 'MCU')) + print(fmt % ('-----', '----', '---')) + for m in models: + print(fmt % (m.num, m.name, m.attiny)) + + print('\nDuplicates:') + for i, m in enumerate(models): + for m2 in models[i+1:]: + #if (m.num == m2.num) and (m is not m2): + if m.num == m2.num: + print('%s\t%s, %s' % (m.num, m.name, m2.name)) + + print('\nMissing:') + for m in models: + if not m.num: + print(m.name) + + +class Empty: + pass + + +def load_cfg(path): + m = Empty() + m.name, m.num, m.attiny = '', '', 'attiny85' + + m.name = path.replace('cfg-', '').replace('.h', '') + + num_pat = re.compile(r'#define\s+MODEL_NUMBER\s+"(\d+)"') + mcu_pat = re.compile(r'ATTINY:\s+(\d+)') + # TODO? use C preprocessor to generate more complete file to scan + with open(path) as fp: + for line in fp: + found = num_pat.search(line) + if found: + m.num = found.group(1) + found = mcu_pat.search(line) + if found: + m.attiny = 'attiny' + found.group(1) + + return m + + +if __name__ == "__main__": + import sys + main(sys.argv[1:]) + diff --git a/bin/steps.py b/bin/steps.py new file mode 100755 index 0000000..e19c9a6 --- /dev/null +++ b/bin/steps.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +"""steps.py: Calculate the stepped ramp levels used by Anduril. +Usage: steps.py floor ceiling num_steps +For example: + > ./steps.py 1 150 3 + 1: 1 + 2: 75 + 3: 150 +""" + +def main(args): + floor, ceil, steps = [int(x) for x in args[:3]] + for i in range(steps): + guess = floor + (i * (float(ceil-floor)/(steps-1))) + this = nearest_level(guess, floor, ceil, steps) + #print('%i: %i (guess: %i)' % (i+1, this, guess)) + print('%i: %i' % (i+1, this)) + + +def nearest_level(target, floor, ceil, steps): + """Copied/adapted from anduril.c""" + # bounds check + # using int16_t here saves us a bunch of logic elsewhere, + # by allowing us to correct for numbers < 0 or > 255 in one central place + mode_min = floor; + mode_max = ceil; + + if (target < mode_min): return mode_min; + if (target > mode_max): return mode_max; + # the rest isn't relevant for smooth ramping + #if (! ramp_style): return target; + + ramp_range = ceil - floor; + ramp_discrete_step_size = int(ramp_range / (steps-1)); + this_level = floor; + + for i in range(steps): + this_level = floor + (i * int(ramp_range) / (steps-1)); + diff = int(target - this_level); + if (diff < 0): diff = -diff; + if (diff <= (ramp_discrete_step_size>>1)): + return this_level; + + return this_level; + + +if __name__ == "__main__": + import sys + main(sys.argv[1:]) + diff --git a/docs/anduril-manual.txt b/docs/anduril-manual.txt new file mode 100644 index 0000000..545af0f --- /dev/null +++ b/docs/anduril-manual.txt @@ -0,0 +1,994 @@ +Anduril User Manual +------------------- + +Anduril is an open-source firmware for flashlights, distributed under +the terms of the GPL v3. The sources can be obtained here: + + http://tiny.cc/TKAnduril + https://bazaar.launchpad.net/~toykeeper/flashlight-firmware/trunk/files/head:/ToyKeeper/spaghetti-monster/anduril/ + + +Quick Start +----------- + +After putting a battery in the light and tightening the parts together, +the light should quickly blink once to confirm it has power and is now +operational. After that, basic usage is simple: + + - Click to turn the light on or off. + - Hold the button to change brightness. + - Release and hold again to change brightness the other way. + +That is all the user needs to know for basic use, but there are many +more modes and features available for people who want more. + +Before reading the rest of this manual, it is recommended that users +look at the Anduril UI diagram(s), which should be provided along with the +flashlight. + + +Button presses +-------------- + +Button presses are abbreviated using a simple notation: + + - 1C: One click. Press and then quickly release the button. + - 1H: Hold. Press the button, but keep holding it. + - 2C: Two clicks. Press and release quickly, twice. + - 2H: Click, hold. Click two times, but hold the second press. + - 3C: Three clicks. Press and release quickly, three times. + - 3H: Click, click, hold. Click three times, but hold the final press. + +The same pattern is used with higher numbers too. For example, 10C +means ten clicks... and 10H means ten clicks but hold the final press. + + +Simple UI +--------- + +By default, the light uses a simple UI. This is useful if you lend the +light to someone else, or if you just don't want to bother with any +crazy disco modes. + +Simple UI has all the basic functions needed to work as a flashlight, +but the minimum and maximum brightness are limited by default to make it +safer, and any complex or advanced functions are blocked. + +Functions available in Simple UI include: + + - 1C: On / off + - 1H: Ramp up (or down, if button was released less than a second ago) + - 2H: If light is on : ramp down + If light is off: momentary high mode + - 2C: Double click to go to / from highest safe level + - 4C: Lockout mode. + +Some other modes and functions are available too. When the light is +off, these are the options: + + - 3C: Battery check mode. (displays voltage once, then turns off) + - 4C: Lockout mode. + - 10H: Switch to Advanced UI. + - 15C or more: Version check. + +In Lockout mode with Simple UI, there are a few functions: + + - 1H: Momentary moon + - 2H: Momentary low + - 3C: Unlock and turn off + - 4C: Unlock and turn on + - 4H: Unlock and turn on at low level + - 5C: Unlock and turn on at high level + +To change between Simple UI and Advanced UI, turn the light off and then +do one of these: + + In Simple UI: + - 10H: Go to Advanced UI. + + In Advanced UI: + - 10C: Go to Simple UI. + - 10H: Configure Simple UI. + +If you get lost, or if you want to auto-calibrate the temperature +sensor, do a factory reset. The process for this is: + + - Loosen tailcap + - Hold button + - Tighten tailcap + - Keep holding button for about 4s + +The light should flicker while getting brighter, then briefly burst to +full power. Hold until it reaches full power to do a reset, or let go +of the button early to abort. + +On some lights, like products where the tailcap method is impossible, +use 13H from Off to do a factory reset. If this is difficult, try +counting it like music to make it easier: + + 1 2 3 4 + 1 2 3 4 + 1 2 3 4 + HOLD + +Simple UI is enabled after each factory reset. + +Simple UI can be configured in several ways, but not while Simple UI is +active. So go to the Advanced UI, configure things, then go back to +Simple UI. + +Configurable options include: + + - floor level + - ceiling level + - number of steps (in stepped ramp) + - turbo style + +Other options are inherited from Advanced UI: + + - ramp style (smooth / stepped) + - smooth ramp speed + - ramp-after-moon style + - memory settings + - auto-lock settings + - aux LED settings + - voltage calibration + - thermal regulation settings + - hardware-specific "misc menu" settings + + +Advanced UI +----------- + +Most of the information below this is for the Advanced UI. Anything not +already noted above is blocked in the Simple UI. + + +Ramping / Stepped Ramping Modes +------------------------------- + +Anduril's ramping mode uses a smooth ramp or a stepped ramp, depending +on which style the user prefers. + +Each ramp has its own settings -- floor (lowest level), ceiling (highest +level), and the stepped ramp can also have a configurable number of +steps. + +Additionally, Simple UI has its own ramp settings for floor, ceiling, +and number of steps. The smooth/stepped style is inherited from the +Advanced UI's ramp. + +There are four ways to access ramping mode when the light is off: + + - 1C: Turn on at the memorized brightness. + (see below for details about what "memorized" means) + + - 1H: Turn on at the floor level. Let go after the light turns on to + stay at the floor level, or keep holding to ramp up. + + - 2C: Turn on at the ceiling level. + + - 2H: Turn on at full power, turn off when released. (momentary turbo) + (in Simple UI, this uses the ceiling level instead of turbo) + +While the light is on, a few actions are available: + + - 1C: Turn off. + - 2C: Go to or from the turbo level. + (or if it has regulated down, "bump" back up to turbo) + (turbo level / behavior is configurable) + - 1H: Change brightness (up). If the button was released less than a + second ago, or if it's already at the ceiling, it goes down instead. + - 2H: Change brightness (down). + + - 3C: Switch to the other ramp style. (smooth / stepped) + (or activate the next channel mode, when more than one is enabled) + (then use 6C instead, for smooth / stepped toggle) + - 6C: Switch to the other ramp style. (when 3C is mapped to next channel) + + - 3H: Momentary turbo (when current channel has no tint to ramp). + - 3H: Tint ramping (only when current channel has adjustable tint). + - 4H: Momentary turbo, when 3H is mapped to tint. + + - 4C: Go to lockout mode. + + - 5C: Go to momentary mode. + - 5H: Start a sunset timer. Details are below in the Sunset Timer section. + + - 7H: Ramp config menu. + - Item 1: Floor level. + - Item 2: Ceiling level. + - Item 3: + Stepped ramp: Number of steps. Can be 1 to 150. + Smooth ramp: Ramp speed. + 1 = Full speed, ~2.5s from end to end. + 2 = Half speed, ~5s from end to end. + 3 = Third speed, ~7.5s. + 4 = Quarter speed, ~10s. + + - 10C: Activate manual memory and save the current brightness. + - 10H: Ramp extras config menu. + - Item 1: Disable manual memory and go back to automatic memory. + (doesn't matter what value the user enters at the prompt) + - Item 2: Configure the manual memory timer. + Sets the timer to N minutes, where N is the number of + clicks. A value of 0 (no clicks) turns the timer off. + - Item 3: Configure whether to ramp up after "Off -> 1H". + 0: Ramp up after moon. + 1: Don't ramp up, just stay at the floor level. + - Item 4: Configure Advanced UI's turbo style: + 0: No turbo, only ceiling. + 1: Anduril 1 style. Ramp -> 2C goes to full power. + 2: Anduril 2 style. Ramp -> 2C goes to ceiling, + or goes to full power if user ramped up to ceiling first. + This value also affects momentary turbo in Ramp and Off modes. + - Item 5: Configure "smooth steps". + 0: Disable smooth steps. + 1: Enable smooth steps. + +Memory determines which brightness level the light goes to with 1 click +from off. There are three types of brightness memory to choose from: + + - Automatic: Always uses the last-ramped brightness. + (does not memorize levels accessed by a shortcut, + like turbo, 2C for ceiling, or 1H-from-off for floor) + + - Manual: Always uses the user's saved brightness. + + - Hybrid: Uses the automatic memory brightness if the light was only + off for a short time, or resets to the manual memory level if it was + off for a longer time. + The timer for this is configurable from 0 to ~140 minutes. + +Another way to think of it is: There are three styles of memory for the +last-ramped brightness level... + + - Always remember (automatic) + - Remember for N minutes (hybrid) + - Never remember (manual) + +To choose a memory style, set the configuration accordingly: + + mem type manual mem manual mem timer + -------- ---------- ---------------- + automatic off any + manual on zero + hybrid on non-zero + +If "smooth steps" is enabled, the stepped ramp uses a smooth animation +between steps, and turning the light on/off has the edges smoothed off +too. With "smooth steps" turned off, these brightness changes are +immediate. + + +Sunset Timer +------------ + +In the ramp mode or candle mode, it's possible to make the light turn +itself off after a while. + +To activate the timer, go to the brightness you want and then use a 5H +action. Keep holding the button, and the light should blink once per +second. Each blink adds 5 minutes to the timer. + +In ramp mode, it slowly dims until it's at the lowest level, then shuts +off. In candle mode, it stays at the same brightness until the final +minute, at which point it dims and dies out. + +The user can change the brightness while the timer is active. If this +happens during the final few minutes, it also "bumps" the timer up to a +minimum of 3 minutes. So if it's getting really dim and you need a +little more time, you could do a 5H to add 5 minutes, or simply ramp up +to the desired brightness. + + +Other Modes +----------- + +Anduril has several other modes too. To access these, press the button +more than 2 times when the light is off: + + - 3C: Blinky / utility modes, starting with battery check. + - 3H: Strobe modes, starting with the most recently used strobe. + - 4C: Lockout mode. + - 5C: Momentary mode. + - 6C: Tactical mode. + - 7C / 7H: Aux LED configuration. + - 9H: Misc Config menu. (only on some lights) + - 10H: Simple UI configuration menu. + - 13H: Factory reset (on some lights). + - 15C or more: Version check. + + +Lockout Mode +------------ + +Click 4 times from Off to enter Lockout mode. Or 4 times from Ramp. +This makes the light safe to carry in a pocket or a bag or anywhere else +the button might be pressed by accident. + +To exit lockout mode, click 4 times. The light should blink briefly and +then turn on at the memorized level. Or hold the final press to turn on +at the floor level instead: + + - 3C: Unlock and go to "Off" mode + + - 4C: Go to ramp mode (memorized level). + (uses manual mem level if there is one) + + - 4H: Go to ramp mode (floor level). + + - 5C: Go to ramp mode (ceiling level). + +Lockout mode also doubles as a momentary moon mode, so the user can do +quick tasks without having to unlock the light. The brightness in +lockout mode has two levels: + + - 1H: Light up at the lowest floor level. + + - 2H: Light up at the highest floor level. + (or the manual mem level, if there is one) + + - 3H: Next channel mode (if more than one is enabled). + +It is also possible to make the light lock itself automatically after +being turned off. To enable this, go to lockout mode and use a 10H +action to activate the auto-lock config menu. Release the button after +the first blink. Then at the prompt, click N times to set the auto-lock +timeout to N minutes. + + - 10H: Auto-lock config menu. Click N times to set timeout to N minutes. + A value of zero disables the auto-lock feature. + So, to turn off auto-lock, don't click at all. + +And on lights which have aux LEDs, there may be additional functions: + + - 7C / 7H: Change Lockout Mode's aux LED pattern. More details on + this below, in a separate section. + + +Blinky / Utility Modes +---------------------- + +Click 3 times from Off to access Anduril's blinky / utility modes. This +always starts at battery check and the user can proceed to other blinky +modes if Advanced UI is enabled. The sequence is: + + - Battery check. + - Temperature check (if light has a temperature sensor). + - Beacon mode. + - SOS mode (if enabled). + +In all of these modes, some basic actions are available: + + - Click: Turn off. + - 2 clicks: Next blinky mode. + +Additionally, in battery check and temperature check modes: + + - 7H: Go to the voltage config menu or thermal config menu. + +In more detail, this is what each blinky / utility mode does: + + Battery check: + + Blinks out the battery voltage per cell. Full is 4.2V, empty is + about 3.0V. The light blinks the whole-number digit first, pauses, + then blinks out the "tenths" digit. Then a longer pause, and it + repeats. + So for 4.2V, it would be "blink-blink-blink-blink .. blink-blink". + + A "zero" digit is represented by a very quick blink. + + On lights with more than one set of LEDs, pressing 3C during batt + check mode can select which set of LEDs (which channel mode) it uses + to blink out numbers. + + The voltage config menu has these settings: + + 1. Voltage correction factor. This adjusts the battery + measurement sensor, allowing the user to add or subtract up to + 0.30V in 0.05V steps. Click N times to enter a value: + + 1C: -0.30V + 2C: -0.25V + 3C: -0.20V + 4C: -0.15V + 5C: -0.10V + 6C: -0.05V + 7C: default, 0V + 8C: +0.05V + 9C: +0.10V + 10C: +0.15V + 11C: +0.20V + 12C: +0.25V + 13C: +0.30V + + 2. Post-off voltage display timeout. (only on lights with RGB aux) + This setting determines how many seconds the RGB aux LEDs + display the voltage color after the torch goes to sleep. Click + once per desired second, or zero times to turn this function + off. + + Temperature check: + + Blinks out the current temperature in degrees C. This number should + be pretty close to what a real thermometer says. If not, it would + be a good idea to enter the thermal config menu and calibrate the + sensor. Or let the light settle to room temperature, then use + factory reset to auto-calibrate the sensor. + + The thermal config menu has two settings: + + - Current temperature. Click once per degree C to calibrate the + sensor. For example, if the ambient temperature is 21 C, then + click 21 times. + + - Temperature limit. This sets the maximum temperature the light + can reach before it will start doing thermal regulation to keep + itself from overheating. Click once per degree C above 30. For + example, to set the limit to 50 C, click 20 times. The default is + 45 C, and the highest value it will allow is 70 C. + + Beacon mode: + + Blinks at a slow speed. The light stays on for 100ms, and then + stays off until the next blink. The brightness and the number of + seconds between pulses are configurable: + + - Brightness is the user's memorized ramp level, so set this in + ramping mode before activating beacon mode. Follows the same + memory rules as ramping -- automatic, manual, or hybrid. + + - Speed is configured by holding the button. The light should + blink once per second while holding the button. Release it + after the desired amount of time has passed, to set a new beacon + speed. + For example, to do a 10-second alpine beacon, hold the button + for 10 seconds. + + SOS mode: + + Blinks out a distress signal. Three short, three long, three short. + Repeats until light is turned off or until battery is low. + + The memorized ramp level determines the brightness of SOS Mode. + + +Strobe / Mood Modes +------------------- + +Anduril includes a few extra modes for a variety of purposes: + + - Candle mode + - Bike flasher + - Party strobe + - Tactical strobe + - Lightning storm mode + +Click 3 times from Off to access these, but hold the third click for a +moment. Click, click, hold. The last-used strobe mode is remembered, +so it will return to whichever one you used last. + +In all of these modes, a few actions are available: + + - 1C: Turn off. + - 2C: Next strobe / mood mode. + - 1H: Increase brightness, or strobe faster. (except lightning) + - 2H: Decrease brightness, or strobe slower. (except lightning) + - 4C: Previous strobe / mood mode. + - 5C: Go to momentary mode, for a momentary strobe. + (this is useful for light painting) + +Additionally, candle mode has one more action: + + - 5H: Activate Sunset Timer, and/or add 5 minutes to the timer. + +In more detail, here is what each mode does: + + - Candle mode + + Brightness changes randomly in a pattern resembling a candle flame. + If a timer is set, it will run until the timer expires, then get + dimmer for one minute, then sputter and turn itself off. Without a + timer, candle mode runs until the user turns it off. Brightness is + configurable. + + - Bike flasher + + Runs at a medium level, but stutters to a brighter level once per + second. Designed to be more visible than a normal ramping mode, but + otherwise works mostly the same. Brightness is configurable. + + - Party strobe + + Motion-freezing strobe light. Can be used to freeze spinning fans + and falling water. Speed is configurable. + + - Tactical strobe + + Disorienting strobe light. Can be used to irritate people. Speed + is configurable, and the duty cycle is always 33%. + + Be careful about heat in this mode, if using it for a long time. + + - Police strobe (on some lights) + + 2-color police style strobe. Only works on lights with 2 or more + colors. + + - Lightning storm mode + + Flashes at random brightness and random speed to simulate lightning + strikes during a busy lightning storm. Do not look directly at the + flashlight when this mode is running, because it may suddenly go to + full power without warning. + + +Momentary Mode +-------------- + +Click 5 times from Off to enter Momentary mode. Or 5 times from Ramp, +or 5 times from a strobe mode. + +This locks the flashlight into a single-mode interface where the LEDs +are only on when the button is held down. It is intended for Morse +code, light painting, and other tasks where the light should be on only +for a short time and probably in a pattern. + +Momentary mode does either a steady brightness level or a strobe, +depending on which was active before going to momentary mode. To select +which one, go to the mode you want to use, adjust the brightness and +speed and other settings, then click 5 times to enter momentary mode. + +In steady mode, brightness is the memorized ramp level, so adjust that +in Ramp Mode before entering momentary mode. + +In momentary strobe mode, the settings are copied from the last-used +strobe mode, such as party strobe, tactical strobe, or lightning. + +To exit this mode, physically disconnect power by unscrewing the tailcap +or battery tube. + + +Tactical Mode +-------------- + +Click 6 times from Off to enter Tactical Mode, or 6 times in Tactical +Mode to exit and go back to "Off". + +Tactical Mode provides instant momentary access to high, low, and +strobe, but each of these is configurable. The inputs are: + + - 1H: High + - 2H: Low + - 3H: Strobe + +Each of these only lasts as long as you hold the button. + +Other commands in Tactical Mode are: + + - 6C: exit (go back to Off Mode) + - 7H: Tactical Mode config menu + - 1st blink: configure tactical slot 1 + - 2nd blink: configure tactical slot 2 + - 3rd blink: configure tactical slot 3 + +To change what is in a tactical slot, press 7H, then release the button +after the 1st, 2nd, or 3rd blink. Then enter a number. Each click adds +1, and each hold adds 10. The number can be: + + - 1 to 150: set the brightness level + - 0: last-used strobe mode + - 151+: go directly to a specific strobe mode + 151 = party strobe + 152 = tactical strobe + 153+ = other strobes, in the same order they're in in the "Off -> 3H" + strobe group + +This assumes the light has a ramp 150 levels long. Strobe modes start +at the ramp size plus 1, so it may be different if a light has a +different ramp size. + + +Configuration Menus +------------------- + +Every config menu has the same interface. It has one or more options +the user can configure, and it will go through them in order. For each +menu item, the light follows the same pattern: + + - Blink once, then go to a lower brightness. The user can keep + holding the button to skip this menu item, or release the button to + dive in and enter a new value. + + - If the user released the button: + + - Stutter or "buzz" quickly between two brightness levels for a few + seconds. This indicates that the user can click one or more times + to enter a number. It will keep buzzing until the user stops + clicking, so there is no need to hurry. + + The actions here are: + - click: add 1 + - hold: add 10 (only in versions 2021-09 or later) + - wait: exit + +After entering a number, or after skipping every menu item, it waits +until the button is released then exits the menu. It should return to +whatever mode the light was in before entering the config menu. + + +Ramp Config Menu +---------------- + +While the light is on in a ramping mode, click 7 times (but hold the +final click) to access the config menu for the current ramp. + +Or, to access the ramp config for Simple UI, make sure the Simple UI is +not active, then do a 10H action from Off. + +For smooth ramping mode, there are three menu options: + + 1. Floor. (default = 1/150) + 2. Ceiling. (default = 120/150) + 3. Ramp speed. (default = 1, fastest speed) + +For the stepped ramping mode, there are three menu options: + + 1. Floor. (default = 20/150) + 2. Ceiling. (default = 120/150) + 3. Number of steps. (default = 7) + +For the Simple UI mode, there are four menu options. The first three +are the same as stepped ramping mode. + + 1. Floor. (default = 20/150) + 2. Ceiling. (default = 120/150) + 3. Number of steps. (default = 5) + 4. Turbo style. (default = 0, no turbo) + +Default values are different for each model of flashlight. The numbers +above are only examples. + +To configure the floor level, click the button equal to the number of +ramp levels (out of 150) at which the floor should be. To set the +lowest possible level, click once. + +To configure the ceiling level, each click goes one level lower. So 1 +click sets the highest possible level, 2 clicks is the 2nd-highest, 3 +clicks is the 3rd-highest level, etc. To set the default of 120/150, +click 31 times. + +When configuring the number of steps, the value can be anything from 1 +to 150. A value of 1 is a special case. It places the step halfway +between the floor and ceiling levels. + + +Version Check Mode +------------------ + +This allows people to see which version of the firmware is installed on +their light. The format for this is (usually) 12 digits -- a model +number and a date. BBPP.YYYY-MM-DD + + - BB: Brand ID + - PP: Product ID + - YYYY: Year + - MM: Month + - DD: Day + +Versions before 2023-05-30 used YYYYMMDDBBPP format. +Anduril 1 used only YYYYMMDD format, or none at all. + +The date is when the firmware was compiled. If the vendor did not set +this value, it defaults to 1969-07-20, the date of first human contact +with the moon. However, it might not be a date at all; some vendors may +specify a completely different type of value here. + +The brand/product values are also known as the model number. These are +hard-coded in the source code for each light's build target, and can be +looked up in the "MODELS" file or by using the "make models" command. + + +Protection Features +------------------- + +Anduril includes low voltage protection (LVP) and thermal regulation. + +LVP makes the light step down to a lower level when the battery is low, +and if the light is already at the lowest level, it shuts itself off. +This activates at 2.8V. LVP adjustments happen suddenly, in large +steps. + +Thermal regulation attempts to keep the light from overheating, and +otherwise adjusts output to stay as close as possible to the +user-configured temperature limit. Thermal adjustments happen +gradually, in steps so small they are difficult for humans to perceive. + + +Aux LEDs / Button LEDs +---------------------- + +Some lights have aux LEDs or button LEDs. These can be configured to do +different things while the main emitters are off. There is one aux LED +mode for the regular "off" mode, and another aux LED mode for "lockout" +mode. This allows the user to see at a glance whether the light is +locked. + +Aux LED modes typically include: + + - Off + - Low + - High + - Blinking + +To configure the aux LEDs, go to the mode you want to configure and then +click the button 7 times. This should change the aux LEDs to the next +mode supported on this light. + + - 7C: Next aux LED mode. + +If the aux LEDs can change color, there are additional actions to change +the color. It is the same as above, but hold the button on the last +click and then let go when the desired color is reached. + + - 7H: Next aux LED color. + +On most lights, the colors follow this sequence: + + - Red + - Yellow (Red+Green) + - Green + - Cyan (Green+Blue) + - Blue + - Purple (Blue+Red) + - White (Red+Green+Blue) + - Disco (fast random colors) + - Rainbow (cycles through all colors in order) + - Voltage (uses color to display battery charge) + +In voltage mode, the colors follow the same sequence, in the same order +as a rainbow... with red indicating a low battery and purple indicating +a full battery. + +For lights with a button LED, the button LED typically stays on while +the main emitters are on. Its brightness level is set in a way which +mirrors the main LED -- off, low, or high. + +For lights with a RGB button LED, the button LED indicates battery +charge during use in the same manner as the aux LED voltage mode. + +For lights with front-facing aux LEDs, the aux LEDs typically stay off +when the main emitters are on, and when the light is otherwise awake. +The aux LEDs on most lights only turn on when the light is asleep. + +When a light has a single-color aux LED and no RGB, it fast-blinks the +aux LED in "off" modes when voltage is low. + + +Misc Config Menu +---------------- + +Some models may have an extra config menu for settings which don't fit +anywhere else. This menu is located at "Off -> 9H" in the advanced UI. + +These settings are, in order: + + - Tint ramp style: (on some lights) + + 0 : smooth ramp (blend channels in any proportion) + 1 : middle tint only + 2 : extreme tints only (only one channel active at a time) + 3+: stepped ramp with 3+ steps + + - Jump Start level: (on some lights) + + Some lights are prone to starting up slowly at low levels, so they + have an option to "jump start" the engine by pulsing a higher power + level for a few milliseconds when changing from off to a low level. + This setting specifies how bright that pulse should be. + + The value can be from 1 to 150, but is usually between 20 and 50. + +These settings are hardware-specific and may not be present on all +lights. The number of settings in the Misc Config Menu depends on the +hardware model and the firmware version. + + +Channel Modes (a.k.a. Tint Ramping or Multi Channel controls) +------------------------------------------------------------- + +Some lights have more than one set of LEDs which can be adjusted to +change the beam color, shape, or other properties. These lights have +features like tint ramping and channel modes. + +On these models, there are some global button mappings which work at all +times unless they're overridden by the mode the light is in: + + - 3C: Next channel mode + - 3H: Adjust current channel mode (ramp tint, for example) + - 9H: Channel mode config menu + +Details depend on the exact type of light used. For example, if a light +has LEDs in cool white, warm white, and red... that light might have a +few channel modes: + + - White blend (adjustable CCT / tint ramping) + - Red only + - Auto-tint + +On a light like this, the user could press 3C to rotate through these +different channel modes... white, then red, then auto, then back to +white. + +Additionally, in the "white blend" mode, the user could press 3H to +manually adjust the balance between warm white and cool white. + +Finally, if the user decides they don't want all of the modes, they can +turn some off. Press 9H (while on) to start the channel mode config +menu. To disable the auto-tint mode, for example, it is the 3rd mode... +so wait for the 3rd blink, then release the button. Then at the prompt, +enter a value of 0 (wait for the prompt to time out without clicking +anything). Afterward, the auto tint mode should no longer be in the +channel mode rotation. To turn the mode back on later, do the same +thing, but enter a value of 1 (click 1 time at the prompt). + +A light can have many different channel modes, so don't be shy about +turning off any modes you don't use. It makes all the others easier to +reach. + +If you turn off channel modes until only 1 remains, the "Ramp -> 3C" +action reverts to its single-channel behavior -- switching between a +smooth or stepped brightness ramp. Additionally, when a channel mode +has nothing to adjust with 3H, the 3H action also reverts to its +single-channel behavior -- momentary turbo. + +The Misc Config Menu (Off -> 9H) may also have a setting to choose a +tint ramp style. There are a few styles available, by entering +different numbers into that config menu: + + 0: smooth ramp + 1: middle tint only + 2: extreme tints only + 3+: stepped ramp with 3+ steps + +This setting only applies to modes with channel ramping (i.e. tint +ramping), and only when that mode uses the default 3H event handler. +Custom channel modes may work differently. + + + +UI Reference Table +------------------ + +This is a table of all button mappings in Anduril, in one place: + +Mode UI Button Action +---- -- ------ ------ +Off Any 1C On (ramp mode, memorized level) +Off Any 1H On (ramp mode, floor level) +Off Any 2C On (ramp mode, ceiling level) +Off Simple 2H On (momentary ceiling level) +Off Full 2H On (momentary turbo) +Off Any 3C Battcheck mode +Off Full 3H Strobe mode (whichever was used last) +Off Any 4C Lockout mode +Off Full 5C Momentary mode +Off Full 6C Tactical mode +Off Full 7C Aux LEDs: Next pattern +Off Full 7H Aux LEDs: Next color +Off Full 9H Misc Config menu (varies per light): + ?1: tint ramp style + ?2: jump start level +Off Full 10C Enable Simple UI +Off Simple 10H Disable Simple UI +Off Full 10H Simple UI ramp config menu: + 1: floor + 2: ceiling + 3: steps + 4: turbo style +Off Any 13H Factory reset (on some lights) +Off Any 15+C Version check + +Ramp Any 1C Off +Ramp Any 1H Ramp (up, with reversing) +Ramp Any 2H Ramp (down) +Ramp Any 2C Go to/from ceiling or turbo (configurable) +Ramp Full 3C Change ramp style (smooth / stepped) +Ramp Full 6C (same as above, but on multi-channel lights) +Ramp Full 3H Momentary turbo (when no tint ramping) +Ramp Full 4H Momentary turbo (on multi channel lights) +Ramp Any 4C Lockout mode +Ramp Full 5C Momentary mode +Ramp Full 5H Sunset timer on, and add 5 minutes +Ramp Full 7H Ramp config menu: (for current ramp) + 1: floor + 2: ceiling + 3: speed / steps +Ramp Full 10C Turn on manual memory and save current brightness +Ramp Full 10H Ramp Extras config menu: + 1: switch to automatic mem, not manual mem + 2: set manual mem timeout + 3: ramp after moon or not + 4: advanced UI turbo style + 5: smooth steps + +Multi-channel lights only: +Any Any 3C Next channel mode (i.e. next color mode) +Any Any 3H Tint ramp (if this mode can) +Any Full 9H Channel mode enable/disable menu: + N: click (or not) to enable (disable) mode N + +Lockout Any 1C/1H Momentary moon (lowest floor) +Lockout Any 2C/2H Momentary moon (highest floor, or manual mem level) +Lockout Any 3C Unlock (go to "Off" mode) +Lockout Any 3H Next channel mode (if more than one enabled) +Lockout Any 4C On (ramp mode, memorized level) +Lockout Any 4H On (ramp mode, floor level) +Lockout Any 5C On (ramp mode, ceiling level) +Lockout Full 7C Aux LEDs: Next pattern +Lockout Full 7H Aux LEDs: Next color +Lockout Full 10H Auto-lock config menu: + 1: set timeout in minutes (0 = no auto-lock) + +Strobe (any) Full 1C Off +Strobe (any) Full 2C Next strobe mode +Strobe (any) Full 3C Next channel mode (saved per strobe mode) +Strobe (any) Full 4C Prev strobe mode +Strobe (any) Full 5C Momentary mode (using current strobe) + +Party strobe Full 1H/2H Faster / slower +Tactical strobe Full 1H/2H Faster / slower +Police strobe - - None (brightness is Ramp Mode's last-used level) +Lightning Full 1H Interrupt current flash or start new one +Candle Full 1H/2H Brighter / dimmer +Candle Full 5H Sunset timer on, add 5 minutes +Biking Full 1H/2H Brighter / dimmer + +Batt check Any 1C Off +Batt check Full 2C Next blinky mode (Temp check, Beacon, SOS) +Batt check Full 3C Next channel mode (for number blinks only) +Batt check Full 7H Voltage config menu + 1: voltage correction factor + ... + 5: -0.10V + 6: -0.05V + 7: no correction + 8: +0.05V + 9: +0.10V + ... + 2: post-off voltage display seconds + +Temp check Full 1C Off +Temp check Full 2C Next blinky mode (Beacon, SOS, Batt check) +Temp check Full 7H Thermal config menu + 1: set current temperature + 2: set temperature limit + +Beacon Full 1C Off +Beacon Full 1H Configure beacon timing +Beacon Full 2C Next blinky mode (SOS, Batt check, Temp check) + +SOS Full 1C Off +SOS Full 2C Next blinky mode (Batt check, Temp check, Beacon) + +Momentary Full Any On (until button is released) +Momentary Full Disconnect power Exit Momentary mode + +Tactical Full 1H High (tactical slot 1) +Tactical Full 2H Low (tactical slot 2) +Tactical Full 3H Strobe (tactical slot 3) +Tactical Full 6C Exit (go back to Off Mode) +Tactical Full 7H Tactical Mode config menu: + 1: tactical slot 1 + 2: tactical slot 2 + 3: tactical slot 3 + +Config menus Full Hold Skip current item with no changes +Config menus Full Release Configure current item + (goes to Number Entry menu) + +Number entry Full Click Add 1 to value for current item +Number entry Full Hold Add 10 to value for current item + diff --git a/docs/anduril-ui.png b/docs/anduril-ui.png new file mode 100644 index 0000000..8f60cc8 Binary files /dev/null and b/docs/anduril-ui.png differ diff --git a/docs/anduril.svg b/docs/anduril.svg new file mode 100644 index 0000000..2ecb530 --- /dev/null +++ b/docs/anduril.svg @@ -0,0 +1,4921 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + Ramps: + Blinkies + Strobes + + + + + + + + + + + + + + + + + + BattCheck + + + + + TempCheck + + + + Beacon + + + + + Sunset + + + + ThermalCfg + + + + + BeaconCfg + + + + Ramp + Ceil + Floor + + + Turbo + + + + Mem + Regulated Hybrid -------------- Direct Drive + + + + + + + + + Ramp Cfg + + 4 Clicks + + + + 4 Clicks + + + + 1. Floor (click N times for level N)2. Ceiling (click N times for Nth highest)3. Number of steps (stepped ramp only) + 1. Current temperature (click N times for N deg C)2. Temperature limit (click N times for N + 30 C) + 1. Beacon speed (click N times for N seconds per flash) + + + Ramp + + Cfg + + + + + + Actions + 1 Click + Hold + 3 Clicks + Other Action + + + + 2 Clicks + Click, Hold + Andúril‎ UI + + + + + + OFF + + + Thermal Cfg + Beacon Cfg + + + + + 4 Clicks + 5 Clicks + Click, Click, Hold (3H) + + + + Brighter/ Faster + + + Dimmer/ Slower + + + Muggle + Momentary + + + 6 Clicks + + + + Smooth + + + + 4 Clicks + + + + + Stepped + + + + + + + + + + + + + + + + Bike + + Flasher + + + Party + + Strobe + + + Tactical + + Strobe + + + Lightning + + Storm + + + Candle + + + + + +30 min + + + + + + + + + + + + + + + + + + + + + OFF + + + + + + + + + + + + + OFF + + While On: + 1C + 1H + 3C + 2C + 2H + Brightness: Off, Low, High, Blinking + + 3C. Smooth /4C. Ramp Cfg5C. Manual Memory5H. Automatic Memory + Stepped ramp + + + + + + + + + + Color: R, Y, G, C, B, V, W, Rainbow, Volts + + + + + Lockout + + 7C + 7H + + Aux /ButtonLEDs + 3C + 3H + 3C + 3 Clicks + 3C + + + + + Lockout + + Momentary Moon / Low + + + + On / Strobes + + Factory Reset: Loosen tailcap, Hold button, Tighten tailcap, Hold 3s (or 13H from Off) + 3C + + + + + + + + diff --git a/fsm/COPYING b/fsm/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/fsm/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/fsm/adc.c b/fsm/adc.c new file mode 100644 index 0000000..31b250f --- /dev/null +++ b/fsm/adc.c @@ -0,0 +1,573 @@ +// fsm-adc.c: ADC (voltage, temperature) functions for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +// override onboard temperature sensor definition, if relevant +#ifdef USE_EXTERNAL_TEMP_SENSOR +#ifdef ADMUX_THERM +#undef ADMUX_THERM +#endif +#define ADMUX_THERM ADMUX_THERM_EXTERNAL_SENSOR +#endif + +#include + + +static inline void set_admux_therm() { + #if (ATTINY == 1634) + ADMUX = ADMUX_THERM; + #elif (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) + ADMUX = ADMUX_THERM | (1 << ADLAR); + #elif (ATTINY == 841) // FIXME: not tested + ADMUXA = ADMUXA_THERM; + ADMUXB = ADMUXB_THERM; + #elif defined(AVRXMEGA3) // ATTINY816, 817, etc + ADC0.MUXPOS = ADC_MUXPOS_TEMPSENSE_gc; // read temperature + ADC0.CTRLC = ADC_SAMPCAP_bm | ADC_PRESC_DIV64_gc | ADC_REFSEL_INTREF_gc; // Internal ADC reference + #else + #error Unrecognized MCU type + #endif + adc_channel = 1; + adc_sample_count = 0; // first result is unstable + ADC_start_measurement(); +} + +inline void set_admux_voltage() { + #if (ATTINY == 1634) + #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 + ADMUX = ADMUX_VOLTAGE_DIVIDER; + #else // VCC / 1.1V reference + ADMUX = ADMUX_VCC; + #endif + #elif (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) + #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 + ADMUX = ADMUX_VOLTAGE_DIVIDER | (1 << ADLAR); + #else // VCC / 1.1V reference + ADMUX = ADMUX_VCC | (1 << ADLAR); + #endif + #elif (ATTINY == 841) // FIXME: not tested + #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 + ADMUXA = ADMUXA_VOLTAGE_DIVIDER; + ADMUXB = ADMUXB_VOLTAGE_DIVIDER; + #else // VCC / 1.1V reference + ADMUXA = ADMUXA_VCC; + ADMUXB = ADMUXB_VCC; + #endif + #elif defined(AVRXMEGA3) // ATTINY816, 817, etc + #ifdef USE_VOLTAGE_DIVIDER // 1.1V / ADC input pin + // verify that this is correct!!! untested + ADC0.MUXPOS = ADMUX_VOLTAGE_DIVIDER; // read the requested ADC pin + ADC0.CTRLC = ADC_SAMPCAP_bm | ADC_PRESC_DIV64_gc | ADC_REFSEL_INTREF_gc; // Use internal ADC reference + #else // VCC / 1.1V reference + ADC0.MUXPOS = ADC_MUXPOS_INTREF_gc; // read internal reference + ADC0.CTRLC = ADC_SAMPCAP_bm | ADC_PRESC_DIV64_gc | ADC_REFSEL_VDDREF_gc; // Vdd (Vcc) be ADC reference + #endif + #else + #error Unrecognized MCU type + #endif + adc_channel = 0; + adc_sample_count = 0; // first result is unstable + ADC_start_measurement(); +} + + +#ifdef TICK_DURING_STANDBY +inline void adc_sleep_mode() { + // needs a special sleep mode to get accurate measurements quickly + // ... full power-down ends up using more power overall, and causes + // some weird issues when the MCU doesn't stay awake enough cycles + // to complete a reading + #ifdef SLEEP_MODE_ADC + // attiny1634 + set_sleep_mode(SLEEP_MODE_ADC); + #elif defined(AVRXMEGA3) // ATTINY816, 817, etc + set_sleep_mode(SLEEP_MODE_STANDBY); + #else + #error No ADC sleep mode defined for this hardware. + #endif +} +#endif + +inline void ADC_start_measurement() { + #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 841) || (ATTINY == 1634) + ADCSRA |= (1 << ADSC) | (1 << ADIE); + #elif defined(AVRXMEGA3) // ATTINY816, 817, etc + ADC0.INTCTRL |= ADC_RESRDY_bm; // enable interrupt + ADC0.COMMAND |= ADC_STCONV_bm; // Start the ADC conversions + #else + #error unrecognized MCU type + #endif +} + +// set up ADC for reading battery voltage +inline void ADC_on() +{ + #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634) + set_admux_voltage(); + #ifdef USE_VOLTAGE_DIVIDER + // disable digital input on divider pin to reduce power consumption + VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); + #else + // disable digital input on VCC pin to reduce power consumption + //VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); // FIXME: unsure how to handle for VCC pin + #endif + #if (ATTINY == 1634) + //ACSRA |= (1 << ACD); // turn off analog comparator to save power + ADCSRB |= (1 << ADLAR); // left-adjust flag is here instead of ADMUX + #endif + // enable, start, auto-retrigger, prescale + ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | ADC_PRSCL; + // end tiny25/45/85 + #elif (ATTINY == 841) // FIXME: not tested, missing left-adjust + ADCSRB = 0; // Right adjusted, auto trigger bits cleared. + //ADCSRA = (1 << ADEN ) | 0b011; // ADC on, prescaler division factor 8. + set_admux_voltage(); + // enable, start, auto-retrigger, prescale + ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | ADC_PRSCL; + //ADCSRA |= (1 << ADSC); // start measuring + #elif defined(AVRXMEGA3) // ATTINY816, 817, etc + VREF.CTRLA |= VREF_ADC0REFSEL_1V1_gc; // Set Vbg ref to 1.1V + // Enabled, free-running (aka, auto-retrigger), run in standby + ADC0.CTRLA = ADC_ENABLE_bm | ADC_FREERUN_bm | ADC_RUNSTBY_bm; + // set a INITDLY value because the AVR manual says so (section 30.3.5) + // (delay 1st reading until Vref is stable) + ADC0.CTRLD |= ADC_INITDLY_DLY16_gc; + set_admux_voltage(); + #else + #error Unrecognized MCU type + #endif +} + +inline void ADC_off() { + #ifdef AVRXMEGA3 // ATTINY816, 817, etc + ADC0.CTRLA &= ~(ADC_ENABLE_bm); // disable the ADC + #else + ADCSRA &= ~(1<> 1; + return result; +} +#endif + +// Each full cycle runs ~2X per second with just voltage enabled, +// or ~1X per second with voltage and temperature. +#if defined(USE_LVP) && defined(USE_THERMAL_REGULATION) +#define ADC_CYCLES_PER_SECOND 1 +#else +#define ADC_CYCLES_PER_SECOND 2 +#endif + +#ifdef AVRXMEGA3 // ATTINY816, 817, etc +#define ADC_vect ADC0_RESRDY_vect +#endif +// happens every time the ADC sampler finishes a measurement +ISR(ADC_vect) { + + #ifdef AVRXMEGA3 // ATTINY816, 817, etc + ADC0.INTFLAGS = ADC_RESRDY_bm; // clear the interrupt + #endif + + if (adc_sample_count) { + + uint16_t m; // latest measurement + uint16_t s; // smoothed measurement + uint8_t channel = adc_channel; + + // update the latest value + #ifdef AVRXMEGA3 // ATTINY816, 817, etc + // Use the factory calibrated values in SIGROW.TEMPSENSE0 and SIGROW.TEMPSENSE1 + // to calculate a temperature reading in Kelvin, then left-align it. + if (channel == 1) { // thermal, convert ADC reading to left-aligned Kelvin + int8_t sigrow_offset = SIGROW.TEMPSENSE1; // Read signed value from signature row + uint8_t sigrow_gain = SIGROW.TEMPSENSE0; // Read unsigned value from signature row + uint32_t temp = ADC0.RES - sigrow_offset; + temp *= sigrow_gain; // Result might overflow 16 bit variable (10bit+8bit) + temp += 0x80; // Add 1/2 to get correct rounding on division below + temp >>= 8; // Divide result to get Kelvin + m = (temp << 6); // left align it + } + else { m = (ADC0.RES << 6); } // voltage, force left-alignment + + #else + m = ADC; + #endif + adc_raw[channel] = m; + + // lowpass the value + //s = adc_smooth[channel]; // easier to read + uint16_t *v = adc_smooth + channel; // compiles smaller + s = *v; + if (m > s) { s++; } + if (m < s) { s--; } + //adc_smooth[channel] = s; + *v = s; + + // track what woke us up, and enable deferred logic + irq_adc = 1; + + } + + // the next measurement isn't the first + adc_sample_count = 1; + // rollover doesn't really matter + //adc_sample_count ++; + +} + +void adc_deferred() { + irq_adc = 0; // event handled + + #ifdef USE_PSEUDO_RAND + // real-world entropy makes this a true random, not pseudo + // Why here instead of the ISR? Because it makes the time-critical ISR + // code a few cycles faster and we don't need crypto-grade randomness. + #ifdef AVRXMEGA3 // ATTINY816, 817, etc + pseudo_rand_seed += ADC0.RESL; // right aligned, not left... so should be equivalent? + #else + pseudo_rand_seed += (ADCL >> 6) + (ADCH << 2); + #endif + #endif + + // the ADC triggers repeatedly when it's on, but we only need to run the + // voltage and temperature regulation stuff once in a while...so disable + // this after each activation, until it's manually enabled again + if (! adc_deferred_enable) return; + + // disable after one iteration + adc_deferred_enable = 0; + + // what is being measured? 0 = battery voltage, 1 = temperature + uint8_t adc_step; + + #if defined(USE_LVP) && defined(USE_THERMAL_REGULATION) + // do whichever one is currently active + adc_step = adc_channel; + #else + // unless there's no temperature sensor... then just do voltage + adc_step = 0; + #endif + + #if defined(TICK_DURING_STANDBY) && defined(USE_SLEEP_LVP) + // in sleep mode, turn off after just one measurement + // (having the ADC on raises standby power by about 250 uA) + // (and the usual standby level is only ~20 uA) + if (go_to_standby) { + ADC_off(); + // if any measurements were in progress, they're done now + adc_active_now = 0; + // also, only check the battery while asleep, not the temperature + adc_channel = 0; + } + #endif + + if (0) {} // placeholder for easier syntax + + #ifdef USE_LVP + else if (0 == adc_step) { // voltage + ADC_voltage_handler(); + #ifdef USE_THERMAL_REGULATION + // set the correct type of measurement for next time + if (! go_to_standby) set_admux_therm(); + #endif + } + #endif + + #ifdef USE_THERMAL_REGULATION + else if (1 == adc_step) { // temperature + ADC_temperature_handler(); + #ifdef USE_LVP + // set the correct type of measurement for next time + set_admux_voltage(); + #endif + } + #endif + + if (adc_reset) adc_reset --; +} + + +#ifdef USE_LVP +static inline void ADC_voltage_handler() { + // rate-limit low-voltage warnings to a max of 1 per N seconds + static uint8_t lvp_timer = 0; + #define LVP_TIMER_START (VOLTAGE_WARNING_SECONDS*ADC_CYCLES_PER_SECOND) // N seconds between LVP warnings + + #ifdef NO_LVP_WHILE_BUTTON_PRESSED + // don't run if button is currently being held + // (because the button causes a reading of zero volts) + if (button_last_state) return; + #endif + + uint16_t measurement; + + // latest ADC value + if (adc_reset) { // just after waking, don't lowpass + measurement = adc_raw[0]; + adc_smooth[0] = measurement; // no lowpass, just use the latest value + } + #ifdef USE_LOWPASS_WHILE_ASLEEP + else if (go_to_standby) { // weaker lowpass while asleep + // occasionally the aux LED color can oscillate during standby, + // while using "voltage" mode ... so try to reduce the oscillation + uint16_t r = adc_raw[0]; + uint16_t s = adc_smooth[0]; + #if 0 + // fixed-rate lowpass, stable but very slow + // (move by only 0.5 ADC units per measurement, 1 ADC unit = 64) + if (r < s) { s -= 32; } + if (r > s) { s += 32; } + #elif 1 + // 1/8th proportional lowpass, faster but less stable + int16_t diff = (r/8) - (s/8); + s += diff; + #else + // 50% proportional lowpass, fastest but least stable + s = (r>>1) + (s>>1); + #endif + adc_smooth[0] = s; + measurement = s; + } + #endif + else measurement = adc_smooth[0]; + + // values stair-step between intervals of 64, with random variations + // of 1 or 2 in either direction, so if we chop off the last 6 bits + // it'll flap between N and N-1... but if we add half an interval, + // the values should be really stable after right-alignment + // (instead of 99.98, 100.00, and 100.02, it'll hit values like + // 100.48, 100.50, and 100.52... which are stable when truncated) + //measurement += 32; + //measurement = (measurement + 16) >> 5; + measurement = (measurement + 16) & 0xffe0; // 1111 1111 1110 0000 + + #ifdef USE_VOLTAGE_DIVIDER + voltage = calc_voltage_divider(measurement); + #else + // calculate actual voltage: volts * 10 + // ADC = 1.1 * 1024 / volts + // volts = 1.1 * 1024 / ADC + voltage = ((uint16_t)(2*1.1*1024*10)/(measurement>>6) + + VOLTAGE_FUDGE_FACTOR + #ifdef USE_VOLTAGE_CORRECTION + + VOLT_CORR - 7 + #endif + ) >> 1; + #endif + + // if low, callback EV_voltage_low / EV_voltage_critical + // (but only if it has been more than N seconds since last call) + if (lvp_timer) { + lvp_timer --; + } else { // it has been long enough since the last warning + #ifdef DUAL_VOLTAGE_FLOOR + if (((voltage < VOLTAGE_LOW) && (voltage > DUAL_VOLTAGE_FLOOR)) || (voltage < DUAL_VOLTAGE_LOW_LOW)) { + #else + if (voltage < VOLTAGE_LOW) { + #endif + // send out a warning + emit(EV_voltage_low, 0); + // reset rate-limit counter + lvp_timer = LVP_TIMER_START; + } + } +} +#endif + + +#ifdef USE_THERMAL_REGULATION +// generally happens once per second while awake +static inline void ADC_temperature_handler() { + // coarse adjustment + #ifndef THERM_LOOKAHEAD + #define THERM_LOOKAHEAD 4 + #endif + // reduce frequency of minor warnings + #ifndef THERM_NEXT_WARNING_THRESHOLD + #define THERM_NEXT_WARNING_THRESHOLD 24 + #endif + // fine-grained adjustment + // how proportional should the adjustments be? + #ifndef THERM_RESPONSE_MAGNITUDE + #define THERM_RESPONSE_MAGNITUDE 64 + #endif + // acceptable temperature window size in C + #define THERM_WINDOW_SIZE 2 + + // TODO? make this configurable per build target? + // (shorter time for hosts with a lower power-to-mass ratio) + // (because then it'll have smaller responses) + #define NUM_TEMP_HISTORY_STEPS 8 // don't change; it'll break stuff + static uint8_t history_step = 0; + static uint16_t temperature_history[NUM_TEMP_HISTORY_STEPS]; + static int8_t warning_threshold = 0; + + if (adc_reset) { // wipe out old data + // ignore average, use latest sample + uint16_t foo = adc_raw[1]; + adc_smooth[1] = foo; + + // forget any past measurements + for(uint8_t i=0; i> 5; + } + + // latest 16-bit ADC reading + uint16_t measurement = adc_smooth[1]; + + // values stair-step between intervals of 64, with random variations + // of 1 or 2 in either direction, so if we chop off the last 6 bits + // it'll flap between N and N-1... but if we add half an interval, + // the values should be really stable after right-alignment + // (instead of 99.98, 100.00, and 100.02, it'll hit values like + // 100.48, 100.50, and 100.52... which are stable when truncated) + //measurement += 32; + measurement = (measurement + 16) >> 5; + //measurement = (measurement + 16) & 0xffe0; // 1111 1111 1110 0000 + + // let the UI see the current temperature in C + // Convert ADC units to Celsius (ish) + #ifndef USE_EXTERNAL_TEMP_SENSOR + // onboard sensor for attiny25/45/85/1634 + temperature = (measurement>>1) + THERM_CAL_OFFSET + (int16_t)TH_CAL - 275; + #else + // external sensor + temperature = EXTERN_TEMP_FORMULA(measurement>>1) + THERM_CAL_OFFSET + (int16_t)TH_CAL; + #endif + + // how much has the temperature changed between now and a few seconds ago? + int16_t diff; + diff = measurement - temperature_history[history_step]; + + // update / rotate the temperature history + temperature_history[history_step] = measurement; + history_step = (history_step + 1) & (NUM_TEMP_HISTORY_STEPS-1); + + // PI[D]: guess what the temperature will be in a few seconds + uint16_t pt; // predicted temperature + pt = measurement + (diff * THERM_LOOKAHEAD); + + // convert temperature limit from C to raw 16-bit ADC units + // C = (ADC>>6) - 275 + THERM_CAL_OFFSET + TH_CAL; + // ... so ... + // (C + 275 - THERM_CAL_OFFSET - TH_CAL) << 6 = ADC; + uint16_t ceil = (TH_CEIL + 275 - TH_CAL - THERM_CAL_OFFSET) << 1; + int16_t offset = pt - ceil; + + // bias small errors toward zero, while leaving large errors mostly unaffected + // (a diff of 1 C is 2 ADC units, * 4 for therm lookahead, so it becomes 8) + // (but a diff of 1 C should only send a warning of magnitude 1) + // (this also makes it only respond to small errors at the time the error + // happened, not after the temperature has stabilized) + for(uint8_t foo=0; foo<3; foo++) { + if (offset > 0) { + offset --; + } else if (offset < 0) { + offset ++; + } + } + + // Too hot? + // (if it's too hot and not getting cooler...) + if ((offset > 0) && (diff > -1)) { + // accumulated error isn't big enough yet to send a warning + if (warning_threshold > 0) { + warning_threshold -= offset; + } else { // error is big enough; send a warning + // how far above the ceiling? + // original method works, but is too slow on some small hosts: + // (and typically has a minimum response magnitude of 2 instead of 1) + // int16_t howmuch = offset; + // ... so increase the amount, except for small values + // (for example, 1:1, 2:1, 3:3, 4:5, 6:9, 8:13, 10:17, 40:77) + // ... and let us tune the response per build target if desired + int16_t howmuch = (offset + offset - 3) * THERM_RESPONSE_MAGNITUDE / 128; + if (howmuch < 1) howmuch = 1; + warning_threshold = THERM_NEXT_WARNING_THRESHOLD - (uint8_t)howmuch; + + // send a warning + emit(EV_temperature_high, howmuch); + } + } + + // Too cold? + // (if it's too cold and still getting colder...) + // the temperature is this far below the floor: + #define BELOW (offset + (THERM_WINDOW_SIZE<<1)) + else if ((BELOW < 0) && (diff < 0)) { + // accumulated error isn't big enough yet to send a warning + if (warning_threshold < 0) { + warning_threshold -= BELOW; + } else { // error is big enough; send a warning + warning_threshold = (-THERM_NEXT_WARNING_THRESHOLD) - BELOW; + + // how far below the floor? + // int16_t howmuch = ((-BELOW) >> 1) * THERM_RESPONSE_MAGNITUDE / 128; + int16_t howmuch = (-BELOW) >> 1; + // send a notification (unless voltage is low) + // (LVP and underheat warnings fight each other) + if (voltage > (VOLTAGE_LOW + 1)) + emit(EV_temperature_low, howmuch); + } + } + #undef BELOW + + // Goldilocks? + // (temperature is within target window, or at least heading toward it) + else { + // send a notification (unless voltage is low) + // (LVP and temp-okay events fight each other) + if (voltage > VOLTAGE_LOW) + emit(EV_temperature_okay, 0); + } +} +#endif + + +#ifdef USE_BATTCHECK +#ifdef BATTCHECK_4bars +PROGMEM const uint8_t voltage_blinks[] = { + 30, 35, 38, 40, 42, 99, +}; +#endif +#ifdef BATTCHECK_6bars +PROGMEM const uint8_t voltage_blinks[] = { + 30, 34, 36, 38, 40, 41, 43, 99, +}; +#endif +#ifdef BATTCHECK_8bars +PROGMEM const uint8_t voltage_blinks[] = { + 30, 33, 35, 37, 38, 39, 40, 41, 42, 99, +}; +#endif +void battcheck() { + #ifdef BATTCHECK_VpT + blink_num(voltage); + #else + uint8_t i; + for(i=0; + voltage >= pgm_read_byte(voltage_blinks + i); + i++) {} + #ifdef DONT_DELAY_AFTER_BATTCHECK + blink_digit(i); + #else + if (blink_digit(i)) + nice_delay_ms(1000); + #endif + #endif +} +#endif + diff --git a/fsm/adc.h b/fsm/adc.h new file mode 100644 index 0000000..1bb67ed --- /dev/null +++ b/fsm/adc.h @@ -0,0 +1,112 @@ +// fsm-adc.h: ADC (voltage, temperature) functions for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#if defined(USE_LVP) || defined(USE_THERMAL_REGULATION) +// use raw value instead of lowpassed value for the next N measurements +// (2 = 1 for voltage + 1 for temperature) +volatile uint8_t adc_reset = 2; +#endif + +#ifdef USE_LVP +// default 5 seconds between low-voltage warning events +#ifndef VOLTAGE_WARNING_SECONDS +#define VOLTAGE_WARNING_SECONDS 5 +#endif +// low-battery threshold in volts * 10 +#ifndef VOLTAGE_LOW +#define VOLTAGE_LOW 29 +#endif +// battery is low but not critical +#ifndef VOLTAGE_RED +#define VOLTAGE_RED 33 +#endif +// MCU sees voltage 0.X volts lower than actual, add X/2 to readings +#ifndef VOLTAGE_FUDGE_FACTOR +#ifdef USE_VOLTAGE_DIVIDER +#define VOLTAGE_FUDGE_FACTOR 0 +#else +#define VOLTAGE_FUDGE_FACTOR 5 +#endif +#endif + +#ifdef TICK_DURING_STANDBY +volatile uint8_t adc_active_now = 0; // sleep LVP needs a different sleep mode +#endif +volatile uint8_t irq_adc = 0; // ADC interrupt happened? +uint8_t adc_sample_count = 0; // skip the first sample; it's junk +uint8_t adc_channel = 0; // 0=voltage, 1=temperature +uint16_t adc_raw[2]; // last ADC measurements (0=voltage, 1=temperature) +uint16_t adc_smooth[2]; // lowpassed ADC measurements (0=voltage, 1=temperature) +// ADC code is split into two parts: +// - ISR: runs immediately at each interrupt, does the bare minimum because time is critical here +// - deferred: the bulk of the logic runs later when time isn't so critical +uint8_t adc_deferred_enable = 0; // stop waiting and run the deferred code +void adc_deferred(); // do the actual ADC-related calculations + +static inline void ADC_voltage_handler(); +uint8_t voltage = 0; +#ifdef USE_VOLTAGE_CORRECTION + #ifdef USE_CFG + #define VOLT_CORR cfg.voltage_correction + #else + // same 0.05V units as fudge factor, + // but 7 is neutral, and the expected range is from 1 to 13 + uint8_t voltage_correction = 7; + #define VOLT_CORR voltage_correction + #endif +#endif +#ifdef USE_LVP +void low_voltage(); +#endif + +#ifdef USE_BATTCHECK +void battcheck(); +#ifdef BATTCHECK_VpT +#define USE_BLINK_NUM +#endif +#if defined(BATTCHECK_8bars) || defined(BATTCHECK_6bars) || defined(BATTCHECK_4bars) +#define USE_BLINK_DIGIT +#endif +#endif +#endif // ifdef USE_LVP + + +#ifdef USE_THERMAL_REGULATION +// try to keep temperature below 45 C +#ifndef DEFAULT_THERM_CEIL +#define DEFAULT_THERM_CEIL 45 +#endif +// don't allow user to set ceiling above 70 C +#ifndef MAX_THERM_CEIL +#define MAX_THERM_CEIL 70 +#endif +// Local per-MCU calibration value +#ifndef THERM_CAL_OFFSET +#define THERM_CAL_OFFSET 0 +#endif +// temperature now, in C (ish) +int16_t temperature; +#ifdef USE_CFG + #define TH_CEIL cfg.therm_ceil + #define TH_CAL cfg.therm_cal_offset +#else + #define TH_CEIL therm_ceil + #define TH_CAL therm_cal_offset + uint8_t therm_ceil = DEFAULT_THERM_CEIL; + int8_t therm_cal_offset = 0; +#endif +static inline void ADC_temperature_handler(); +#endif // ifdef USE_THERMAL_REGULATION + + +inline void ADC_on(); +inline void ADC_off(); +inline void ADC_start_measurement(); + +#ifdef TICK_DURING_STANDBY +inline void adc_sleep_mode(); +#endif + diff --git a/fsm/chan-aux.c b/fsm/chan-aux.c new file mode 100644 index 0000000..e04e6a2 --- /dev/null +++ b/fsm/chan-aux.c @@ -0,0 +1,11 @@ +// channel modes for single color aux LEDs +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +void set_level_aux(uint8_t level) { + indicator_led(!(!(level)) << 1); // high (or off) +} + +bool gradual_tick_null(uint8_t gt) { return true; } // do nothing + diff --git a/fsm/chan-aux.h b/fsm/chan-aux.h new file mode 100644 index 0000000..ff599b8 --- /dev/null +++ b/fsm/chan-aux.h @@ -0,0 +1,25 @@ +// channel modes for single color aux LEDs +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define NUM_AUX_CHANNEL_MODES 1 + +// include / exclude field based on compile options +#ifdef USE_CHANNEL_MODE_ARGS + #define AUX_HAS_ARGS , .has_args = 0 +#else + #define AUX_HAS_ARGS +#endif + +#define AUX_CHANNELS \ + { \ + .set_level = set_level_aux, \ + .gradual_tick = gradual_tick_null \ + AUX_HAS_ARGS \ + } + +void set_level_aux(uint8_t level); + +bool gradual_tick_null(uint8_t gt); + diff --git a/fsm/chan-rgbaux.c b/fsm/chan-rgbaux.c new file mode 100644 index 0000000..19d18a6 --- /dev/null +++ b/fsm/chan-rgbaux.c @@ -0,0 +1,35 @@ +// channel modes for RGB aux LEDs +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +void set_level_auxred(uint8_t level) { + rgb_led_set(!(!(level)) * 0b000010); // red, high (or off) +} + +void set_level_auxyel(uint8_t level) { + rgb_led_set(!(!(level)) * 0b001010); // red+green, high (or off) +} + +void set_level_auxgrn(uint8_t level) { + rgb_led_set(!(!(level)) * 0b001000); // green, high (or off) +} + +void set_level_auxcyn(uint8_t level) { + rgb_led_set(!(!(level)) * 0b101000); // green+blue, high (or off) +} + +void set_level_auxblu(uint8_t level) { + rgb_led_set(!(!(level)) * 0b100000); // blue, high (or off) +} + +void set_level_auxprp(uint8_t level) { + rgb_led_set(!(!(level)) * 0b100010); // red+blue, high (or off) +} + +void set_level_auxwht(uint8_t level) { + rgb_led_set(!(!(level)) * 0b101010); // red+green+blue, high (or off) +} + +bool gradual_tick_null(uint8_t gt) { return true; } // do nothing + diff --git a/fsm/chan-rgbaux.h b/fsm/chan-rgbaux.h new file mode 100644 index 0000000..6ef5d89 --- /dev/null +++ b/fsm/chan-rgbaux.h @@ -0,0 +1,72 @@ +// channel modes for RGB aux LEDs +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define RGB_AUX_ENUMS \ + CM_AUXRED, \ + CM_AUXYEL, \ + CM_AUXGRN, \ + CM_AUXCYN, \ + CM_AUXBLU, \ + CM_AUXPRP, \ + CM_AUXWHT + +#define RGB_AUX_CM_ARGS 0,0,0,0,0,0,0 + +#define NUM_RGB_AUX_CHANNEL_MODES 7 + +// include / exclude field based on compile options +#ifdef USE_CHANNEL_MODE_ARGS + #define AUX_RGB_HAS_ARGS , .has_args = 0 +#else + #define AUX_RGB_HAS_ARGS +#endif + +#define RGB_AUX_CHANNELS \ + { \ + .set_level = set_level_auxred, \ + .gradual_tick = gradual_tick_null \ + AUX_RGB_HAS_ARGS \ + }, \ + { \ + .set_level = set_level_auxyel, \ + .gradual_tick = gradual_tick_null \ + AUX_RGB_HAS_ARGS \ + }, \ + { \ + .set_level = set_level_auxgrn, \ + .gradual_tick = gradual_tick_null \ + AUX_RGB_HAS_ARGS \ + }, \ + { \ + .set_level = set_level_auxcyn, \ + .gradual_tick = gradual_tick_null \ + AUX_RGB_HAS_ARGS \ + }, \ + { \ + .set_level = set_level_auxblu, \ + .gradual_tick = gradual_tick_null \ + AUX_RGB_HAS_ARGS \ + }, \ + { \ + .set_level = set_level_auxprp, \ + .gradual_tick = gradual_tick_null \ + AUX_RGB_HAS_ARGS \ + }, \ + { \ + .set_level = set_level_auxwht, \ + .gradual_tick = gradual_tick_null \ + AUX_RGB_HAS_ARGS \ + } + +void set_level_auxred(uint8_t level); +void set_level_auxyel(uint8_t level); +void set_level_auxgrn(uint8_t level); +void set_level_auxcyn(uint8_t level); +void set_level_auxblu(uint8_t level); +void set_level_auxprp(uint8_t level); +void set_level_auxwht(uint8_t level); + +bool gradual_tick_null(uint8_t gt); + diff --git a/fsm/channels.c b/fsm/channels.c new file mode 100644 index 0000000..cc78536 --- /dev/null +++ b/fsm/channels.c @@ -0,0 +1,357 @@ +// fsm-channels.c: Channel mode functions for SpaghettiMonster. +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "fsm-ramping.h" + + +#if NUM_CHANNEL_MODES > 1 +void set_channel_mode(uint8_t mode) { + if (mode == channel_mode) return; // abort if nothing to do + + uint8_t cur_level = actual_level; + + // turn off old LEDs before changing channel + set_level(0); + + // change the channel + channel_mode = mode; + + // update the LEDs + set_level(cur_level); +} +#endif // if NUM_CHANNEL_MODES > 1 + + +#ifdef USE_CALC_2CH_BLEND +// calculate a "tint ramp" blend between 2 channels +// results are placed in *warm and *cool vars +// brightness : total amount of light units to distribute +// top : maximum allowed brightness per channel +// blend : ratio between warm and cool (0 = warm, 128 = 50%, 255 = cool) +void calc_2ch_blend( + PWM_DATATYPE *warm, + PWM_DATATYPE *cool, + PWM_DATATYPE brightness, + PWM_DATATYPE top, + uint8_t blend) { + + #ifndef TINT_RAMPING_CORRECTION + #define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint + #endif + + // calculate actual PWM levels based on a single-channel ramp + // and a blend value + PWM_DATATYPE warm_PWM, cool_PWM; + PWM_DATATYPE2 base_PWM = brightness; + + #if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0) + uint8_t level = actual_level - 1; + + // middle tints sag, so correct for that effect + // by adding extra power which peaks at the middle tint + // (correction is only necessary when PWM is fast) + if (level > HALFSPEED_LEVEL) { + base_PWM = brightness + + ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64) + * triangle_wave(blend) / 255); + } + // fade the triangle wave out when above 100% power, + // so it won't go over 200% + if (brightness > top) { + base_PWM -= 2 * ( + ((brightness - top) * TINT_RAMPING_CORRECTION / 64) + * triangle_wave(blend) / 255 + ); + } + // guarantee no more than 200% power + if (base_PWM > (top << 1)) { base_PWM = top << 1; } + #endif + + cool_PWM = (((PWM_DATATYPE2)blend * (PWM_DATATYPE2)base_PWM) + 127) / 255; + warm_PWM = base_PWM - cool_PWM; + // when running at > 100% power, spill extra over to other channel + if (cool_PWM > top) { + warm_PWM += (cool_PWM - top); + cool_PWM = top; + } else if (warm_PWM > top) { + cool_PWM += (warm_PWM - top); + warm_PWM = top; + } + + *warm = warm_PWM; + *cool = cool_PWM; +} +#endif // ifdef USE_CALC_2CH_BLEND + + +#ifdef USE_HSV2RGB +RGB_t hsv2rgb(uint8_t h, uint8_t s, uint16_t v) { + RGB_t color; + + if (s == 0) { // grey + color.r = color.g = color.b = v; + return color; + } + + uint8_t region; + uint16_t fpart; + uint16_t high, low, rising, falling; + + // hue has 6 segments, 0-5 + region = ((uint16_t)h * 6) >> 8; + // find remainder part, make it from 0-255 + fpart = ((uint16_t)h * 6) - (region << 8); + + // calculate graph segments, doing integer multiplication + // TODO: calculate 16-bit results, not 8-bit + high = v; + low = ((uint32_t)v * (255 - s)) >> 8; + // TODO: use a cosine crossfade instead of linear + // (because it looks better and feels more natural) + falling = ((uint32_t)v * (255 - ((s * fpart) >> 8))) >> 8; + rising = ((uint32_t)v * (255 - ((s * (255 - fpart)) >> 8))) >> 8; + + // default floor + color.r = low; + color.g = low; + color.b = low; + + // assign graph shapes based on color cone region + switch (region) { + case 0: + color.r = high; + color.g = rising; + //color.b = low; + break; + case 1: + color.r = falling; + color.g = high; + //color.b = low; + break; + case 2: + //color.r = low; + color.g = high; + color.b = rising; + break; + case 3: + //color.r = low; + color.g = falling; + color.b = high; + break; + case 4: + color.r = rising; + //color.g = low; + color.b = high; + break; + default: + color.r = high; + //color.g = low; + color.b = falling; + break; + } + + return color; +} +#endif // ifdef USE_HSV2RGB + + +///// Common set_level_*() functions shared by multiple lights ///// +// (unique lights should use their own, +// but these common versions cover most of the common hardware designs) + +// TODO: upgrade some older lights to dynamic PWM +// TODO: 1ch w/ dynamic PWM +// TODO: 1ch w/ dynamic PWM and opamp enable pins? +// TODO: 2ch stacked w/ dynamic PWM +// TODO: 2ch stacked w/ dynamic PWM and opamp enable pins? + + +#ifdef USE_SET_LEVEL_1CH +// single set of LEDs with 1 power channel +void set_level_1ch(uint8_t level) { + if (level == 0) { + LOW_PWM_LVL = 0; + } else { + level --; // PWM array index = level - 1 + LOW_PWM_LVL = PWM_GET(low_pwm_levels, level); + } +} +#endif + + +#ifdef USE_SET_LEVEL_2CH_STACKED +// single set of LEDs with 2 stacked power channels, DDFET+1 or DDFET+linear +void set_level_2ch_stacked(uint8_t level) { + if (level == 0) { + LOW_PWM_LVL = 0; + HIGH_PWM_LVL = 0; + } else { + level --; // PWM array index = level - 1 + LOW_PWM_LVL = PWM_GET(low_pwm_levels, level); + HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level); + } +} +#endif + + +#ifdef USE_SET_LEVEL_3CH_STACKED +// single set of LEDs with 3 stacked power channels, like DDFET+N+1 +void set_level_3ch_stacked(uint8_t level) { + if (level == 0) { + LOW_PWM_LVL = 0; + MED_PWM_LVL = 0; + HIGH_PWM_LVL = 0; + } else { + level --; // PWM array index = level - 1 + LOW_PWM_LVL = PWM_GET(low_pwm_levels, level); + MED_PWM_LVL = PWM_GET(med_pwm_levels, level); + HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level); + } +} +#endif + + +#if defined(USE_TINT_RAMPING) && (!defined(TINT_RAMP_TOGGLE_ONLY)) +void set_level_2ch_blend() { + #ifndef TINT_RAMPING_CORRECTION + #define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint + #endif + + // calculate actual PWM levels based on a single-channel ramp + // and a global tint value + //PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level); + uint16_t brightness = PWM1_LVL; + uint16_t warm_PWM, cool_PWM; + #ifdef USE_STACKED_DYN_PWM + uint16_t top = PWM1_TOP; + //uint16_t top = PWM_GET(pwm_tops, actual_level-1); + #else + const uint16_t top = PWM_TOP; + #endif + + // auto-tint modes + uint8_t mytint; + uint8_t level = actual_level - 1; + #if 1 + // perceptual by ramp level + if (tint == 0) { mytint = 255 * (uint16_t)level / RAMP_SIZE; } + else if (tint == 255) { mytint = 255 - (255 * (uint16_t)level / RAMP_SIZE); } + #else + // linear with power level + //if (tint == 0) { mytint = brightness; } + //else if (tint == 255) { mytint = 255 - brightness; } + #endif + // stretch 1-254 to fit 0-255 range (hits every value except 98 and 198) + else { mytint = (tint * 100 / 99) - 1; } + + PWM_DATATYPE2 base_PWM = brightness; + #if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0) + // middle tints sag, so correct for that effect + // by adding extra power which peaks at the middle tint + // (correction is only necessary when PWM is fast) + if (level > HALFSPEED_LEVEL) { + base_PWM = brightness + + ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64) * triangle_wave(mytint) / 255); + } + // fade the triangle wave out when above 100% power, + // so it won't go over 200% + if (brightness > top) { + base_PWM -= 2 * ( + ((brightness - top) * TINT_RAMPING_CORRECTION / 64) + * triangle_wave(mytint) / 255 + ); + } + // guarantee no more than 200% power + if (base_PWM > (top << 1)) { base_PWM = top << 1; } + #endif + + cool_PWM = (((PWM_DATATYPE2)mytint * (PWM_DATATYPE2)base_PWM) + 127) / 255; + warm_PWM = base_PWM - cool_PWM; + // when running at > 100% power, spill extra over to other channel + if (cool_PWM > top) { + warm_PWM += (cool_PWM - top); + cool_PWM = top; + } else if (warm_PWM > top) { + cool_PWM += (warm_PWM - top); + warm_PWM = top; + } + + TINT1_LVL = warm_PWM; + TINT2_LVL = cool_PWM; + + // disable the power channel, if relevant + #ifdef LED_ENABLE_PIN + if (warm_PWM) + LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN); + else + LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN); + #endif + #ifdef LED2_ENABLE_PIN + if (cool_PWM) + LED2_ENABLE_PORT |= (1 << LED2_ENABLE_PIN); + else + LED2_ENABLE_PORT &= ~(1 << LED2_ENABLE_PIN); + #endif +} +#endif // ifdef USE_TINT_RAMPING + + +#ifdef USE_GRADUAL_TICK_1CH +void gradual_tick_1ch() { + GRADUAL_TICK_SETUP(); + + GRADUAL_ADJUST_1CH(low_pwm_levels, LOW_PWM_LVL); + + // did we go far enough to hit the next defined ramp level? + // if so, update the main ramp level tracking var + if ((LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt))) + { + GRADUAL_IS_ACTUAL(); + } +} +#endif + + +#ifdef USE_GRADUAL_TICK_2CH_STACKED +void gradual_tick_2ch_stacked() { + GRADUAL_TICK_SETUP(); + + GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP); + GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL); + + // did we go far enough to hit the next defined ramp level? + // if so, update the main ramp level tracking var + if ( (LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt)) + && (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt)) + ) + { + GRADUAL_IS_ACTUAL(); + } +} +#endif + + +#ifdef USE_GRADUAL_TICK_3CH_STACKED +void gradual_tick_3ch_stacked() { + GRADUAL_TICK_SETUP(); + + GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP); + GRADUAL_ADJUST(med_pwm_levels, MED_PWM_LVL, PWM_TOP); + GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL); + + // did we go far enough to hit the next defined ramp level? + // if so, update the main ramp level tracking var + if ( (LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt)) + && (MED_PWM_LVL == PWM_GET(med_pwm_levels, gt)) + && (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt)) + ) + { + GRADUAL_IS_ACTUAL(); + } +} +#endif + + diff --git a/fsm/channels.h b/fsm/channels.h new file mode 100644 index 0000000..218f4f5 --- /dev/null +++ b/fsm/channels.h @@ -0,0 +1,141 @@ +// fsm-channels.h: Channel mode functions for SpaghettiMonster. +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +// always enable channel modes, even if there is only one +#define USE_CHANNEL_MODES + +// typedefs +typedef void SetLevelFunc(uint8_t level); +typedef SetLevelFunc * SetLevelFuncPtr; + +typedef bool GradualTickFunc(uint8_t gt); +typedef GradualTickFunc * GradualTickFuncPtr; + +// TODO: implement custom 3H handlers +typedef void ChannelArgFunc(); +typedef ChannelArgFunc * ChannelArgFuncPtr; + +typedef struct Channel { + SetLevelFuncPtr set_level; + #ifdef USE_SET_LEVEL_GRADUALLY + GradualTickFuncPtr gradual_tick; + #endif + #ifdef USE_CUSTOM_3H_HANDLERS + // TODO: implement custom 3H handlers + ChannelArgFuncPtr ramp_channel_arg; + #endif + #ifdef USE_CHANNEL_MODE_ARGS + bool has_args; + //uint8_t arg; // is in the config struct, not here + #endif +} Channel; + +Channel channels[]; // values are defined in the hwdef-*.c + +// TODO: size-optimize the case with only 1 channel mode? +// (the arrays and stuff shouldn't be needed) + +#if NUM_CHANNEL_MODES > 1 + #define USE_CHANNEL_MODES + // current multi-channel mode + uint8_t channel_mode = DEFAULT_CHANNEL_MODE; +#else + #define channel_mode 0 +#endif + +#ifdef USE_CUSTOM_CHANNEL_3H_MODES +// different 3H behavior per channel? +// TODO: move to progmem +// TODO: move to Anduril, not FSM +StatePtr channel_3H_modes[NUM_CHANNEL_MODES]; +#endif + +//#ifdef USE_CHANNEL_MODE_TOGGLES +#if NUM_CHANNEL_MODES > 1 +// user can take unwanted modes out of the rotation +// bitmask +#ifdef USE_CFG + #define channel_mode_enabled(n) ((cfg.channel_modes_enabled >> n) & 1) + #define channel_mode_enable(n) cfg.channel_modes_enabled |= (1 << n) + #define channel_mode_disable(n) cfg.channel_modes_enabled &= ((1 << n) ^ 0xff) +#else + uint16_t channel_modes_enabled = CHANNEL_MODES_ENABLED; + #define channel_mode_enabled(n) ((channel_modes_enabled >> n) & 1) + #define channel_mode_enable(n) channel_modes_enabled |= (1 << n) + #define channel_mode_disable(n) channel_modes_enabled &= ((1 << n) ^ 0xff) + #endif +#endif + +#ifdef USE_CHANNEL_MODE_ARGS + #ifndef USE_CFG + // one byte of extra data per channel mode, like for tint value + uint8_t channel_mode_args[NUM_CHANNEL_MODES] = { CHANNEL_MODE_ARGS }; + #endif + // which modes respond to their "arg", and which don't? + //const uint8_t channel_has_args = CHANNEL_HAS_ARGS; + //#define channel_has_args(n) ((CHANNEL_HAS_ARGS >> n) & 1) + // struct member + #define channel_has_args(n) (channels[n].has_args) +#endif + +#if NUM_CHANNEL_MODES > 1 +void set_channel_mode(uint8_t mode); +#endif + +#ifdef USE_CALC_2CH_BLEND +void calc_2ch_blend( + PWM_DATATYPE *warm, + PWM_DATATYPE *cool, + PWM_DATATYPE brightness, + PWM_DATATYPE top, + uint8_t blend); +#endif + +#ifdef USE_HSV2RGB +typedef struct RGB_t { + uint16_t r; + uint16_t g; + uint16_t b; +} RGB_t; +RGB_t hsv2rgb(uint8_t h, uint8_t s, uint16_t v); +#endif // ifdef USE_HSV2RGB + + +#ifdef USE_SET_LEVEL_1CH +// TODO: remove this +void set_level_1ch(uint8_t level); +#endif + +#ifdef USE_SET_LEVEL_2CH_STACKED +// TODO: remove this +void set_level_2ch_stacked(uint8_t level); +#endif + +#ifdef USE_SET_LEVEL_3CH_STACKED +// TODO: remove this +void set_level_3ch_stacked(uint8_t level); +#endif + +#if defined(USE_TINT_RAMPING) && (!defined(TINT_RAMP_TOGGLE_ONLY)) +// TODO: remove this +void set_level_2ch_blend(); +#endif + +#ifdef USE_GRADUAL_TICK_1CH +// TODO: remove this +void gradual_tick_1ch(); +#endif + +#ifdef USE_GRADUAL_TICK_2CH_STACKED +// TODO: remove this +void gradual_tick_2ch_stacked(); +#endif + +#ifdef USE_GRADUAL_TICK_3CH_STACKED +// TODO: remove this +void gradual_tick_3ch_stacked(); +#endif + diff --git a/fsm/eeprom.c b/fsm/eeprom.c new file mode 100644 index 0000000..66cdd78 --- /dev/null +++ b/fsm/eeprom.c @@ -0,0 +1,112 @@ +// fsm-eeprom.c: EEPROM API for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "fsm-eeprom.h" + +#ifdef USE_EEPROM +#ifdef EEPROM_OVERRIDE +uint8_t *eeprom; +#else +uint8_t eeprom[EEPROM_BYTES]; +#endif + +uint8_t load_eeprom() { + #if defined(LED_ENABLE_PIN) || defined(LED2_ENABLE_PIN) + delay_4ms(2); // wait for power to stabilize + #endif + + cli(); + // check if eeprom has been initialized; abort if it hasn't + uint8_t marker = eeprom_read_byte((uint8_t *)EEP_START); + if (marker != EEP_MARKER) { sei(); return 0; } + + // load the actual data + for(uint8_t i=0; i (uint8_t *)(EEP_WL_SIZE-EEPROM_WL_BYTES-1)) offset = 0; + eep_wl_prev_offset = offset; + // marker byte + // FIXME: write the marker last, to signal completed transaction + eeprom_update_byte(offset, EEP_MARKER); + offset ++; + // user data + for(uint8_t i=0; i + +// set this higher to enable normal eeprom functions +#ifndef EEPROM_BYTES +#define EEPROM_BYTES 0 +#endif + +// set this higher to enable wear-levelled eeprom functions +#ifndef EEPROM_WL_BYTES +#define EEPROM_WL_BYTES 0 +#endif + +#ifdef USE_EEPROM +// this fails when EEPROM_BYTES is a sizeof() +//#if EEPROM_BYTES >= (EEPSIZE/2) +//#error Requested EEPROM_BYTES too big. +//#endif +#ifdef EEPROM_OVERRIDE +uint8_t *eeprom; +#else +uint8_t eeprom[EEPROM_BYTES]; +#endif +uint8_t load_eeprom(); // returns 1 for success, 0 for no data found +void save_eeprom(); +#define EEP_START (EEPSIZE/2) +#endif + +#ifdef USE_EEPROM_WL +#if EEPROM_WL_BYTES >= (EEPSIZE/4) +#error Requested EEPROM_WL_BYTES too big. +#endif +uint8_t eeprom_wl[EEPROM_WL_BYTES]; +uint8_t load_eeprom_wl(); // returns 1 for success, 0 for no data found +void save_eeprom_wl(); +#define EEP_WL_SIZE (EEPSIZE/2) +#endif + +#if EEPSIZE > 256 +#define EEP_OFFSET_T uint16_t +#else +#define EEP_OFFSET_T uint8_t +#endif + +// if this marker isn't found, the eeprom is assumed to be blank +#define EEP_MARKER 0b10100101 + diff --git a/fsm/events.c b/fsm/events.c new file mode 100644 index 0000000..6987ae2 --- /dev/null +++ b/fsm/events.c @@ -0,0 +1,198 @@ +// fsm-events.c: Event-handling functions for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + + +void append_emission(Event event, uint16_t arg) { + uint8_t i; + // find last entry + for(i=0; + (i=0; i--) { + uint8_t err = state_stack[i](event, arg); + if (! err) return 0; + } + return 1; // event not handled +} + +void emit(Event event, uint16_t arg) { + // add this event to the queue for later, + // so we won't use too much time during an interrupt + append_emission(event, arg); +} + +void emit_current_event(uint16_t arg) { + emit(current_event, arg); +} + +void empty_event_sequence() { + current_event = EV_none; + ticks_since_last_event = 0; + // when the user completes an input sequence, interrupt any running timers + // to cancel any delays currently in progress + // This eliminates a whole bunch of extra code: + // before: if (! nice_delay_ms(ms)) {break;} + // after: nice_delay_ms(ms); + interrupt_nice_delays(); +} + +uint8_t push_event(uint8_t ev_type) { // only for use by PCINT_inner() + // don't do this here; do it in PCINT_inner() instead + //ticks_since_last_event = 0; // something happened + + // only click events are sent to this function + current_event |= B_CLICK; + + // handle button presses + if (ev_type == B_PRESS) { + // set press flag + current_event |= B_PRESS; + // increase click counter + if ((current_event & B_COUNT) < (B_COUNT)) { + current_event ++; + } + return 1; // event pushed, even if max clicks already reached + // (will just repeat the max over and over) + } + // handle button releases + else if (ev_type == B_RELEASE) { + // clear the press flag + current_event &= (~B_PRESS); + // if a "hold" event just ended, set the timeout flag + // to indicate that the event is done and can be cleared + if (current_event & B_HOLD) { current_event |= B_TIMEOUT; } + return 1; // event pushed + } + + return 0; // unexpected event type +} + + +// explicitly interrupt these "nice" delays +volatile uint8_t nice_delay_interrupt = 0; +inline void interrupt_nice_delays() { nice_delay_interrupt = 1; } + +// like delay_ms, except it aborts on state change +// return value: +// 0: state changed +// 1: normal completion +uint8_t nice_delay_ms(uint16_t ms) { + /* // delay_zero() implementation + if (ms == 0) { + CLKPR = 1< 0) { + if (nice_delay_interrupt) { + return 0; + } + + #ifdef USE_DYNAMIC_UNDERCLOCKING + #ifdef USE_RAMPING + uint8_t level = actual_level; // volatile, avoid repeat access + if (level < QUARTERSPEED_LEVEL) { + clock_prescale_set(clock_div_4); + _delay_loop_2(BOGOMIPS*DELAY_FACTOR/100/4); + } + //else if (level < HALFSPEED_LEVEL) { + // clock_prescale_set(clock_div_2); + // _delay_loop_2(BOGOMIPS*95/100/2); + //} + else { + clock_prescale_set(clock_div_1); + _delay_loop_2(BOGOMIPS*DELAY_FACTOR/100); + } + // restore regular clock speed + clock_prescale_set(clock_div_1); + #else + // underclock MCU to save power + clock_prescale_set(clock_div_4); + // wait + _delay_loop_2(BOGOMIPS*DELAY_FACTOR/100/4); + // restore regular clock speed + clock_prescale_set(clock_div_1); + #endif // ifdef USE_RAMPING + #else + // wait + _delay_loop_2(BOGOMIPS*DELAY_FACTOR/100); + #endif // ifdef USE_DYNAMIC_UNDERCLOCKING + + // run pending system processes while we wait + handle_deferred_interrupts(); + + // handle events only afterward, so that any collapsed delays will + // finish running the UI's loop() code before taking any further actions + // (this helps make sure code runs in the correct order) + // (otherwise, a new state's EV_enter runs before the old state's + // loop() has finished, and things can get weird) + process_emissions(); + } + return 1; +} + +#ifdef USE_DYNAMIC_UNDERCLOCKING +void delay_4ms(uint8_t ms) { + while(ms-- > 0) { + // underclock MCU to save power + clock_prescale_set(clock_div_4); + // wait + _delay_loop_2(BOGOMIPS*98/100); + // restore regular clock speed + clock_prescale_set(clock_div_1); + } +} +#else +void delay_4ms(uint8_t ms) { + while(ms-- > 0) { + // wait + _delay_loop_2(BOGOMIPS*398/100); + } +} +#endif +/* +uint8_t nice_delay_4ms(uint8_t ms) { + return nice_delay_ms((uint16_t)ms << 2); +} +*/ + +/* +uint8_t nice_delay_s() { + return nice_delay_4ms(250); +} +*/ + diff --git a/fsm/events.h b/fsm/events.h new file mode 100644 index 0000000..575af1b --- /dev/null +++ b/fsm/events.h @@ -0,0 +1,221 @@ +// fsm-events.h: Event-handling functions for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + + +// timeout durations in ticks (each tick 1/62th s) +#ifndef HOLD_TIMEOUT +#define HOLD_TIMEOUT 24 +#endif +#ifndef RELEASE_TIMEOUT +#define RELEASE_TIMEOUT 18 +#endif + +// return codes for Event handlers +// Indicates whether this handler consumed (handled) the Event, or +// if the Event should be sent to the next handler in the stack. +#define EVENT_HANDLED 0 +#define EVENT_NOT_HANDLED 1 +#define MISCHIEF_MANAGED EVENT_HANDLED +#define MISCHIEF_NOT_MANAGED EVENT_NOT_HANDLED + +// typedefs +typedef uint8_t Event; +typedef struct Emission { + Event event; + uint16_t arg; +} Emission; + +Event current_event; +// at 0.016 ms per tick, 255 ticks = 4.08 s +static volatile uint16_t ticks_since_last_event = 0; + +// maximum number of events which can be waiting at one time +// (would probably be okay to reduce this to 4, but it's higher to be safe) +#define EMISSION_QUEUE_LEN 16 +// was "volatile" before, changed to regular var since IRQ rewrites seem +// to have removed the need for it to be volatile +// no comment about "volatile emissions" +Emission emissions[EMISSION_QUEUE_LEN]; + +void append_emission(Event event, uint16_t arg); +void delete_first_emission(); +void process_emissions(); +uint8_t emit_now(Event event, uint16_t arg); +void emit(Event event, uint16_t arg); +void emit_current_event(uint16_t arg); +void empty_event_sequence(); +uint8_t push_event(uint8_t ev_type); // only for use by PCINT_inner() + + +// TODO: Maybe move these to their own file... +// ... this probably isn't the right place for delays. +#ifndef DELAY_FACTOR + // adjust the timing of delays, lower = shorter delays + // 90 = 90% delay, 10% for other things + #define DELAY_FACTOR 92 +#endif +inline void interrupt_nice_delays(); +uint8_t nice_delay_ms(uint16_t ms); +//uint8_t nice_delay_s(); +void delay_4ms(uint8_t ms); + + +/* Event structure + * Bit 7: 1 for a button input event, 0 for all others. + * If bit 7 is 1: + * Bits 0,1,2,3: Click counter. Up to 15 clicks. + * Bit 4: 1 for a "press" event, 0 for a "release" event. + * Bit 5: 1 for a "hold" event, 0 otherwise. + * Bit 6: 1 for a "timeout" event, 0 otherwise. + * If bit 7 is 0: + * Sort of ad-hoc, shown in #defines below. + */ + +// event masks / bits +#define B_SYSTEM 0b00000000 +#define B_CLICK 0b10000000 +#define B_TIMEOUT 0b01000000 +#define B_HOLD 0b00100000 +#define B_PRESS 0b00010000 +#define B_RELEASE 0b00000000 +#define B_COUNT 0b00001111 +#define B_FLAGS 0b11110000 + +// Event types +#define EV_none 0 + +// Events which aren't button presses +#define EV_debug (B_SYSTEM|0b01111111) +#define EV_enter_state (B_SYSTEM|0b00001000) +#define EV_leave_state (B_SYSTEM|0b00001001) +#define EV_reenter_state (B_SYSTEM|0b00001010) +#define EV_tick (B_SYSTEM|0b00000001) +#ifdef TICK_DURING_STANDBY +#define EV_sleep_tick (B_SYSTEM|0b00000011) +#endif +#ifdef USE_LVP +#define EV_voltage_low (B_SYSTEM|0b00000100) +#endif +#ifdef USE_THERMAL_REGULATION +#define EV_temperature_high (B_SYSTEM|0b00000101) +#define EV_temperature_low (B_SYSTEM|0b00000110) +#define EV_temperature_okay (B_SYSTEM|0b00000111) +#endif + +// Button press events + +// shouldn't normally happen, but UI might empty_event_sequence() while button +// is down so a release with no recorded prior hold could be possible +#define EV_release (B_CLICK|B_RELEASE|0) + +#define EV_click1_press (B_CLICK|B_PRESS|1) +#define EV_click1_release (B_CLICK|B_RELEASE|1) +#define EV_click1_complete (B_CLICK|B_TIMEOUT|1) +#define EV_1click EV_click1_complete +#define EV_click1_hold (B_CLICK|B_HOLD|B_PRESS|1) +#define EV_click1_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|1) +#define EV_hold EV_click1_hold + +#define EV_click2_press (B_CLICK|B_PRESS|2) +#define EV_click2_release (B_CLICK|B_RELEASE|2) +#define EV_click2_complete (B_CLICK|B_TIMEOUT|2) +#define EV_2clicks EV_click2_complete +#define EV_click2_hold (B_CLICK|B_HOLD|B_PRESS|2) +#define EV_click2_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|2) + +#define EV_click3_press (B_CLICK|B_PRESS|3) +#define EV_click3_release (B_CLICK|B_RELEASE|3) +#define EV_click3_complete (B_CLICK|B_TIMEOUT|3) +#define EV_3clicks EV_click3_complete +#define EV_click3_hold (B_CLICK|B_HOLD|B_PRESS|3) +#define EV_click3_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|3) + +#define EV_click4_press (B_CLICK|B_PRESS|4) +#define EV_click4_release (B_CLICK|B_RELEASE|4) +#define EV_click4_complete (B_CLICK|B_TIMEOUT|4) +#define EV_4clicks EV_click4_complete +#define EV_click4_hold (B_CLICK|B_HOLD|B_PRESS|4) +#define EV_click4_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|4) + +#define EV_click5_press (B_CLICK|B_PRESS|5) +#define EV_click5_release (B_CLICK|B_RELEASE|5) +#define EV_click5_complete (B_CLICK|B_TIMEOUT|5) +#define EV_5clicks EV_click5_complete +#define EV_click5_hold (B_CLICK|B_HOLD|B_PRESS|5) +#define EV_click5_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|5) + +#define EV_click6_press (B_CLICK|B_PRESS|6) +#define EV_click6_release (B_CLICK|B_RELEASE|6) +#define EV_click6_complete (B_CLICK|B_TIMEOUT|6) +#define EV_6clicks EV_click6_complete +#define EV_click6_hold (B_CLICK|B_HOLD|B_PRESS|6) +#define EV_click6_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|6) + +#define EV_click7_press (B_CLICK|B_PRESS|7) +#define EV_click7_release (B_CLICK|B_RELEASE|7) +#define EV_click7_complete (B_CLICK|B_TIMEOUT|7) +#define EV_7clicks EV_click7_complete +#define EV_click7_hold (B_CLICK|B_HOLD|B_PRESS|7) +#define EV_click7_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|7) + +#define EV_click8_press (B_CLICK|B_PRESS|8) +#define EV_click8_release (B_CLICK|B_RELEASE|8) +#define EV_click8_complete (B_CLICK|B_TIMEOUT|8) +#define EV_8clicks EV_click8_complete +#define EV_click8_hold (B_CLICK|B_HOLD|B_PRESS|8) +#define EV_click8_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|8) + +#define EV_click9_press (B_CLICK|B_PRESS|9) +#define EV_click9_release (B_CLICK|B_RELEASE|9) +#define EV_click9_complete (B_CLICK|B_TIMEOUT|9) +#define EV_9clicks EV_click9_complete +#define EV_click9_hold (B_CLICK|B_HOLD|B_PRESS|9) +#define EV_click9_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|9) + +#define EV_click10_press (B_CLICK|B_PRESS|10) +#define EV_click10_release (B_CLICK|B_RELEASE|10) +#define EV_click10_complete (B_CLICK|B_TIMEOUT|10) +#define EV_10clicks EV_click10_complete +#define EV_click10_hold (B_CLICK|B_HOLD|B_PRESS|10) +#define EV_click10_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|10) + +#define EV_click11_press (B_CLICK|B_PRESS|11) +#define EV_click11_release (B_CLICK|B_RELEASE|11) +#define EV_click11_complete (B_CLICK|B_TIMEOUT|11) +#define EV_11clicks EV_click11_complete +#define EV_click11_hold (B_CLICK|B_HOLD|B_PRESS|11) +#define EV_click11_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|11) + +#define EV_click12_press (B_CLICK|B_PRESS|12) +#define EV_click12_release (B_CLICK|B_RELEASE|12) +#define EV_click12_complete (B_CLICK|B_TIMEOUT|12) +#define EV_12clicks EV_click12_complete +#define EV_click12_hold (B_CLICK|B_HOLD|B_PRESS|12) +#define EV_click12_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|12) + +#define EV_click13_press (B_CLICK|B_PRESS|13) +#define EV_click13_release (B_CLICK|B_RELEASE|13) +#define EV_click13_complete (B_CLICK|B_TIMEOUT|13) +#define EV_13clicks EV_click13_complete +#define EV_click13_hold (B_CLICK|B_HOLD|B_PRESS|13) +#define EV_click13_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|13) + +#define EV_click14_press (B_CLICK|B_PRESS|14) +#define EV_click14_release (B_CLICK|B_RELEASE|14) +#define EV_click14_complete (B_CLICK|B_TIMEOUT|14) +#define EV_14clicks EV_click14_complete +#define EV_click14_hold (B_CLICK|B_HOLD|B_PRESS|14) +#define EV_click14_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|14) + +#define EV_click15_press (B_CLICK|B_PRESS|15) +#define EV_click15_release (B_CLICK|B_RELEASE|15) +#define EV_click15_complete (B_CLICK|B_TIMEOUT|15) +#define EV_15clicks EV_click15_complete +#define EV_click15_hold (B_CLICK|B_HOLD|B_PRESS|15) +#define EV_click15_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|15) + diff --git a/fsm/main.c b/fsm/main.c new file mode 100644 index 0000000..066188c --- /dev/null +++ b/fsm/main.c @@ -0,0 +1,211 @@ +// fsm-main.c: main() function for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "fsm-main.h" + +#if PWM_CHANNELS == 4 +#ifdef AVRXMEGA3 // ATTINY816, 817, etc +#error 4-channel PWM not currently set up for the AVR 1-Series +#endif +// 4th PWM channel requires manually turning the pin on/off via interrupt :( +ISR(TIMER1_OVF_vect) { + //bitClear(PORTB, 3); + PORTB &= 0b11110111; + //PORTB |= 0b00001000; +} +ISR(TIMER1_COMPA_vect) { + //if (!bitRead(TIFR,TOV1)) bitSet(PORTB, 3); + if (! (TIFR & (1<= 1 + DDRB |= (1 << PWM1_PIN); + TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) + TCCR0A = PHASE; + #if (PWM1_PIN == PB4) // Second PWM counter is ... weird + TCCR1 = _BV (CS10); + GTCCR = _BV (COM1B1) | _BV (PWM1B); + OCR1C = 255; // Set ceiling value to maximum + #endif + #endif + // tint ramping needs second channel enabled, + // despite PWM_CHANNELS being only 1 + #if (PWM_CHANNELS >= 2) || defined(USE_TINT_RAMPING) + DDRB |= (1 << PWM2_PIN); + #if (PWM2_PIN == PB4) // Second PWM counter is ... weird + TCCR1 = _BV (CS10); + GTCCR = _BV (COM1B1) | _BV (PWM1B); + OCR1C = 255; // Set ceiling value to maximum + #endif + #endif + #if PWM_CHANNELS >= 3 + DDRB |= (1 << PWM3_PIN); + #if (PWM3_PIN == PB4) // Second PWM counter is ... weird + TCCR1 = _BV (CS10); + GTCCR = _BV (COM1B1) | _BV (PWM1B); + OCR1C = 255; // Set ceiling value to maximum + #endif + #endif + #if PWM_CHANNELS >= 4 + // 4th PWM channel is ... not actually supported in hardware :( + DDRB |= (1 << PWM4_PIN); + //OCR1C = 255; // Set ceiling value to maximum + TCCR1 = 1<= 1 + PWM1_LVL = 0; + #endif + #if PWM_CHANNELS >= 2 + PWM2_LVL = 0; + #endif + #if PWM_CHANNELS >= 3 + PWM3_LVL = 0; + #endif + #if PWM_CHANNELS >= 4 + PWM4_LVL = 255; // inverted :( + #endif + #endif + standby_mode(); + } + + // catch up on interrupts + handle_deferred_interrupts(); + + // turn delays back on, if they were off + nice_delay_interrupt = 0; + + // give the recipe some time slices + loop(); + + } +} + + +void handle_deferred_interrupts() { + /* + if (irq_pcint) { // button pressed or released + // nothing to do here + // (PCINT only matters during standby) + } + */ + if (irq_adc) { // ADC done measuring + adc_deferred(); + // irq_adc = 0; // takes care of itself + } + if (irq_wdt) { // the clock ticked + WDT_inner(); + // irq_wdt = 0; // takes care of itself + } +} + diff --git a/fsm/main.h b/fsm/main.h new file mode 100644 index 0000000..2e2a111 --- /dev/null +++ b/fsm/main.h @@ -0,0 +1,10 @@ +// fsm-main.h: main() function for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +int main(); +// needs to run frequently to execute the logic for WDT and ADC and stuff +void handle_deferred_interrupts(); + diff --git a/fsm/misc.c b/fsm/misc.c new file mode 100644 index 0000000..bc10ea1 --- /dev/null +++ b/fsm/misc.c @@ -0,0 +1,312 @@ +// fsm-misc.c: Miscellaneous function for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#ifdef USE_DYNAMIC_UNDERCLOCKING +void auto_clock_speed() { + uint8_t level = actual_level; // volatile, avoid repeat access + if (level < QUARTERSPEED_LEVEL) { + // run at quarter speed + // note: this only works when executed as two consecutive instructions + // (don't try to combine them or put other stuff between) + clock_prescale_set(clock_div_4); + } + else if (level < HALFSPEED_LEVEL) { + // run at half speed + clock_prescale_set(clock_div_2); + } else { + // run at full speed + clock_prescale_set(clock_div_1); + } +} +#endif + +#if defined(USE_BLINK_NUM) || defined(USE_BLINK_DIGIT) +#define BLINK_SPEED 1000 +uint8_t blink_digit(uint8_t num) { + //StatePtr old_state = current_state; + + // "zero" digit gets a single short blink + uint8_t ontime = BLINK_SPEED * 2 / 12; + if (!num) { ontime = BLINK_ONCE_TIME; num ++; } + + #ifdef BLINK_CHANNEL + // channel is set per blink, to prevent issues + // if another mode interrupts us (like a config menu) + uint8_t old_channel = channel_mode; + #endif + + for (; num>0; num--) { + // TODO: allow setting a blink channel mode per build target + #ifdef BLINK_CHANNEL + set_channel_mode(BLINK_CHANNEL); + #endif + set_level(BLINK_BRIGHTNESS); + #ifdef BLINK_CHANNEL + channel_mode = old_channel; + #endif + nice_delay_ms(ontime); + + #ifdef BLINK_CHANNEL + set_channel_mode(BLINK_CHANNEL); + #endif + set_level(0); + #ifdef BLINK_CHANNEL + channel_mode = old_channel; + #endif + nice_delay_ms(BLINK_SPEED * 3 / 12); + } + + #ifdef BLINK_CHANNEL + set_channel_mode(old_channel); + #endif + + return nice_delay_ms(BLINK_SPEED * 8 / 12); +} +#endif + +#ifdef USE_BLINK_BIG_NUM +uint8_t blink_big_num(uint16_t num) { + uint16_t digits[] = { 10000, 1000, 100, 10, 1 }; + uint8_t started = 0; + for (uint8_t digit=0; digit= scale) { + started = 1; + } + if (started) { + uint8_t digit = 0; + while (num >= scale) { + num -= scale; + digit ++; + } + if (! blink_digit(digit)) return 0; + } + } + + return nice_delay_ms(1000); +} +#endif +#ifdef USE_BLINK_NUM +uint8_t blink_num(uint8_t num) { + #if 1 + uint8_t hundreds = num / 100; + num = num % 100; + uint8_t tens = num / 10; + num = num % 10; + #else // can be smaller or larger, depending on whether divmod is used elsewhere + uint8_t hundreds = 0; + uint8_t tens = 0; + for(; num >= 100; hundreds ++, num -= 100); + for(; num >= 10; tens ++, num -= 10); + #endif + + #if 0 + // wait a moment in the dark before starting + set_level(0); + nice_delay_ms(200); + #endif + + if (hundreds) blink_digit(hundreds); + if (hundreds || tens) blink_digit(tens); + return blink_digit(num); +} +#endif + +#ifdef USE_INDICATOR_LED +void indicator_led(uint8_t lvl) { + switch (lvl) { + #ifdef AVRXMEGA3 // ATTINY816, 817, etc + + case 0: // indicator off + AUXLED_PORT.DIRSET = (1 << AUXLED_PIN); // set as output + AUXLED_PORT.OUTCLR = (1 << AUXLED_PIN); // set output low + #ifdef AUXLED2_PIN // second LED mirrors the first + AUXLED2_PORT.DIRSET = (1 << AUXLED2_PIN); // set as output + AUXLED2_PORT.OUTCLR = (1 << AUXLED2_PIN); // set output low + #endif + break; + case 1: // indicator low + AUXLED_PORT.DIRCLR = (1 << AUXLED_PIN); // set as input + // this resolves to PORTx.PINxCTRL = PORT_PULLUPEN_bm; + *((uint8_t *)&AUXLED_PORT + 0x10 + AUXLED_PIN) = PORT_PULLUPEN_bm; // enable internal pull-up + #ifdef AUXLED2_PIN // second LED mirrors the first + AUXLED2_PORT.DIRCLR = (1 << AUXLED2_PIN); // set as input + // this resolves to PORTx.PINxCTRL = PORT_PULLUPEN_bm; + *((uint8_t *)&AUXLED2_PORT + 0x10 + AUXLED2_PIN) = PORT_PULLUPEN_bm; // enable internal pull-up + #endif + break; + default: // indicator high + AUXLED_PORT.DIRSET = (1 << AUXLED_PIN); // set as output + AUXLED_PORT.OUTSET = (1 << AUXLED_PIN); // set as high + #ifdef AUXLED2_PIN // second LED mirrors the first + AUXLED2_PORT.DIRSET = (1 << AUXLED2_PIN); // set as output + AUXLED2_PORT.OUTSET = (1 << AUXLED2_PIN); // set as high + #endif + break; + + #else // MCU is old tiny style, not newer mega style + + case 0: // indicator off + DDRB &= 0xff ^ (1 << AUXLED_PIN); + PORTB &= 0xff ^ (1 << AUXLED_PIN); + #ifdef AUXLED2_PIN // second LED mirrors the first + DDRB &= 0xff ^ (1 << AUXLED2_PIN); + PORTB &= 0xff ^ (1 << AUXLED2_PIN); + #endif + break; + case 1: // indicator low + DDRB &= 0xff ^ (1 << AUXLED_PIN); + PORTB |= (1 << AUXLED_PIN); + #ifdef AUXLED2_PIN // second LED mirrors the first + DDRB &= 0xff ^ (1 << AUXLED2_PIN); + PORTB |= (1 << AUXLED2_PIN); + #endif + break; + default: // indicator high + DDRB |= (1 << AUXLED_PIN); + PORTB |= (1 << AUXLED_PIN); + #ifdef AUXLED2_PIN // second LED mirrors the first + DDRB |= (1 << AUXLED2_PIN); + PORTB |= (1 << AUXLED2_PIN); + #endif + break; + + #endif // MCU type + } +} + +/* +void indicator_led_auto() { + if (actual_level > MAX_1x7135) indicator_led(2); + else if (actual_level > 0) indicator_led(1); + else indicator_led(0); +} +*/ +#endif // USE_INDICATOR_LED + +#ifdef USE_BUTTON_LED +// TODO: Refactor this and RGB LED function to merge code and save space +void button_led_set(uint8_t lvl) { + switch (lvl) { + + #ifdef AVRXMEGA3 // ATTINY816, 817, etc + + case 0: // LED off + BUTTON_LED_PORT.DIRSET = (1 << BUTTON_LED_PIN); // set as output + BUTTON_LED_PORT.OUTCLR = (1 << BUTTON_LED_PIN); // set output low + break; + case 1: // LED low + BUTTON_LED_PORT.DIRCLR = (1 << BUTTON_LED_PIN); // set as input + // this resolves to PORTx.PINxCTRL = PORT_PULLUPEN_bm; + *((uint8_t *)&BUTTON_LED_PORT + 0x10 + BUTTON_LED_PIN) = PORT_PULLUPEN_bm; // enable internal pull-up + break; + default: // LED high + BUTTON_LED_PORT.DIRSET = (1 << BUTTON_LED_PIN); // set as output + BUTTON_LED_PORT.OUTSET = (1 << BUTTON_LED_PIN); // set as high + break; + + #else + + case 0: // LED off + BUTTON_LED_DDR &= 0xff ^ (1 << BUTTON_LED_PIN); + BUTTON_LED_PUE &= 0xff ^ (1 << BUTTON_LED_PIN); + BUTTON_LED_PORT &= 0xff ^ (1 << BUTTON_LED_PIN); + break; + case 1: // LED low + BUTTON_LED_DDR &= 0xff ^ (1 << BUTTON_LED_PIN); + BUTTON_LED_PUE |= (1 << BUTTON_LED_PIN); + BUTTON_LED_PORT |= (1 << BUTTON_LED_PIN); + break; + default: // LED high + BUTTON_LED_DDR |= (1 << BUTTON_LED_PIN); + BUTTON_LED_PUE |= (1 << BUTTON_LED_PIN); + BUTTON_LED_PORT |= (1 << BUTTON_LED_PIN); + break; + + #endif // MCU type + } +} +#endif + +#ifdef USE_AUX_RGB_LEDS +void rgb_led_set(uint8_t value) { + // value: 0b00BBGGRR + uint8_t pins[] = { AUXLED_R_PIN, AUXLED_G_PIN, AUXLED_B_PIN }; + for (uint8_t i=0; i<3; i++) { + uint8_t lvl = (value >> (i<<1)) & 0x03; + uint8_t pin = pins[i]; + switch (lvl) { + + #ifdef AVRXMEGA3 // ATTINY816, 817, etc + + case 0: // LED off + AUXLED_RGB_PORT.DIRSET = (1 << pin); // set as output + AUXLED_RGB_PORT.OUTCLR = (1 << pin); // set output low + break; + case 1: // LED low + AUXLED_RGB_PORT.DIRCLR = (1 << pin); // set as input + // this resolves to PORTx.PINxCTRL = PORT_PULLUPEN_bm; + *((uint8_t *)&AUXLED_RGB_PORT + 0x10 + pin) = PORT_PULLUPEN_bm; // enable internal pull-up + break; + default: // LED high + AUXLED_RGB_PORT.DIRSET = (1 << pin); // set as output + AUXLED_RGB_PORT.OUTSET = (1 << pin); // set as high + break; + + #else + + case 0: // LED off + AUXLED_RGB_DDR &= 0xff ^ (1 << pin); + AUXLED_RGB_PUE &= 0xff ^ (1 << pin); + AUXLED_RGB_PORT &= 0xff ^ (1 << pin); + break; + case 1: // LED low + AUXLED_RGB_DDR &= 0xff ^ (1 << pin); + AUXLED_RGB_PUE |= (1 << pin); + AUXLED_RGB_PORT |= (1 << pin); + break; + default: // LED high + AUXLED_RGB_DDR |= (1 << pin); + AUXLED_RGB_PUE |= (1 << pin); + AUXLED_RGB_PORT |= (1 << pin); + break; + + #endif // MCU type + } + } +} +#endif // ifdef USE_AUX_RGB_LEDS + +#ifdef USE_TRIANGLE_WAVE +uint8_t triangle_wave(uint8_t phase) { + uint8_t result = phase << 1; + if (phase > 127) result = 255 - result; + return result; +} +#endif + +#ifdef USE_REBOOT +void reboot() { + // put the WDT in hard reset mode, then trigger it + cli(); + #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) + WDTCR = 0xD8 | WDTO_15MS; + #elif (ATTINY == 1634) + // allow protected configuration changes for next 4 clock cycles + CCP = 0xD8; // magic number + // reset (WDIF + WDE), no WDIE, fastest (16ms) timing (0000) + // (DS section 8.5.2 and table 8-4) + WDTCSR = 0b10001000; + #elif defined(AVRXMEGA3) // ATTINY816, 817, etc + CCP = CCP_IOREG_gc; // temporarily disable change protection + WDT.CTRLA = WDT_PERIOD_8CLK_gc; // Enable, timeout 8ms + #endif + sei(); + wdt_reset(); + while (1) {} +} +#endif + diff --git a/fsm/misc.h b/fsm/misc.h new file mode 100644 index 0000000..8de6b29 --- /dev/null +++ b/fsm/misc.h @@ -0,0 +1,68 @@ +// fsm-misc.h: Miscellaneous function for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#ifdef USE_DYNAMIC_UNDERCLOCKING +void auto_clock_speed(); +#endif + +// shortest time (in ms) the light should blink for to indicate a zero +#ifndef BLINK_ONCE_TIME + #define BLINK_ONCE_TIME 10 +#endif + +#if defined(USE_BLINK_NUM) || defined(USE_BLINK_DIGIT) + #ifndef BLINK_BRIGHTNESS + #define BLINK_BRIGHTNESS (MAX_LEVEL/6) + #endif + #if defined(USE_CFG) && defined(DEFAULT_BLINK_CHANNEL) + #define BLINK_CHANNEL cfg.blink_channel + #elif defined(DEFAULT_BLINK_CHANNEL) + #define BLINK_CHANNEL DEFAULT_BLINK_CHANNEL + #endif + uint8_t blink_digit(uint8_t num); +#endif + +#ifdef USE_BLINK_NUM +//#define USE_BLINK +uint8_t blink_num(uint8_t num); +#endif + +/* +#ifdef USE_BLINK +uint8_t blink(uint8_t num, uint8_t speed); +#endif +*/ + +#ifdef USE_INDICATOR_LED +// FIXME: Remove this, replace with button_led() +// lvl: 0=off, 1=low, 2=high +void indicator_led(uint8_t lvl); +#endif + +#ifdef USE_BUTTON_LED +// lvl: 0=off, 1=low, 2=high +void button_led_set(uint8_t lvl); +#endif + +// if any type of aux LEDs exist, define a shorthand flag for it +#if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS) || defined(USE_BUTTON_LED) +#define HAS_AUX_LEDS +#endif + +#ifdef USE_AUX_RGB_LEDS +// value: 0b00BBGGRR +// each pair of bits: 0=off, 1=low, 2=high +void rgb_led_set(uint8_t value); +#endif + +#ifdef USE_TRIANGLE_WAVE +uint8_t triangle_wave(uint8_t phase); +#endif + +#ifdef USE_REBOOT +void reboot(); +#endif + diff --git a/fsm/pcint.c b/fsm/pcint.c new file mode 100644 index 0000000..131d0c3 --- /dev/null +++ b/fsm/pcint.c @@ -0,0 +1,96 @@ +// fsm-pcint.c: PCINT (Pin Change Interrupt) functions for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +uint8_t button_is_pressed() { + uint8_t value = ((SWITCH_PORT & (1< 0) + (level > DEFAULT_LEVEL)); + #endif + #ifdef USE_BUTTON_LED + button_led_set((level > 0) + (level > DEFAULT_LEVEL)); + #endif + } + #else // turn off front-facing aux LEDs while main LEDs are on + #if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS) + if (! go_to_standby) { + #ifdef USE_INDICATOR_LED + indicator_led(0); + #endif + #ifdef USE_AUX_RGB_LEDS + rgb_led_set(0); + #ifdef USE_BUTTON_LED + button_led_set((level > 0) + (level > DEFAULT_LEVEL)); + #endif + #endif + } + #endif + #endif +} +#endif // ifdef HAS_AUX_LEDS + +#ifdef USE_AUX_RGB_LEDS_WHILE_ON +// TODO: maybe move this stuff into FSM +#include "anduril/aux-leds.h" // for rgb_led_voltage_readout() +inline void set_level_aux_rgb_leds(uint8_t level) { + if (! go_to_standby) { + if (level > 0) { + rgb_led_voltage_readout(level > USE_AUX_RGB_LEDS_WHILE_ON); + } else { + rgb_led_set(0); + } + // some drivers can be wired with RGB or single color to button + // ... so support both even though only one is connected + #ifdef USE_BUTTON_LED + button_led_set((level > 0) + (level > DEFAULT_LEVEL)); + #endif + } +} +#endif // ifdef USE_AUX_RGB_LEDS_WHILE_ON + + +void set_level(uint8_t level) { + #ifdef USE_JUMP_START + // maybe "jump start" the engine, if it's prone to slow starts + // (pulse the output high for a moment to wake up the power regulator) + // (only do this when starting from off and going to a low level) + // TODO: allow different jump start behavior per channel mode + // FIXME: don't jump-start during factory reset + // (it seems to cause some eeprom issues on KR4 + // when doing a click with a loose tailcap) + if ((! actual_level) + && level + && (level < JUMP_START_LEVEL)) { + set_level(JUMP_START_LEVEL); + delay_4ms(JUMP_START_TIME/4); + } + #endif + + #ifdef HAS_AUX_LEDS + set_level_aux_leds(level); + #endif + + #ifdef USE_AUX_RGB_LEDS_WHILE_ON + set_level_aux_rgb_leds(level); + #endif + + if (0 == level) { + set_level_zero(); + } else { + // call the relevant hardware-specific set_level_*() + SetLevelFuncPtr set_level_func = channels[channel_mode].set_level; + set_level_func(level - 1); + } + + if (actual_level != level) prev_level = actual_level; + actual_level = level; + + #ifdef USE_SET_LEVEL_GRADUALLY + gradual_target = level; + #endif + + #ifdef USE_DYNAMIC_UNDERCLOCKING + auto_clock_speed(); + #endif +} + +#ifdef USE_LEGACY_SET_LEVEL +// (this is mostly just here for reference, temporarily) +// single set of LEDs with 1 to 3 stacked power channels, +// like linear, FET+1, and FET+N+1 +// (default set_level_*() function for most lights) +void set_level_legacy(uint8_t level) { + if (level == 0) { + #if PWM_CHANNELS >= 1 + PWM1_LVL = 0; + #endif + #if PWM_CHANNELS >= 2 + PWM2_LVL = 0; + #endif + #if PWM_CHANNELS >= 3 + PWM3_LVL = 0; + #endif + #if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_OFF) + PWM1_CNT = 0; + #endif + #if defined(PWM2_CNT) && defined(PWM2_PHASE_RESET_OFF) + PWM2_CNT = 0; + #endif + #if defined(PWM3_CNT) && defined(PWM3_PHASE_RESET_OFF) + PWM3_CNT = 0; + #endif + #ifdef LED_OFF_DELAY + // for drivers with a slow regulator chip (eg, boost converter), + // delay before turning off to prevent flashes + delay_4ms(LED_OFF_DELAY/4); + #endif + // disable the power channel, if relevant + #ifdef LED_ENABLE_PIN + LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN); + #endif + #ifdef LED2_ENABLE_PIN + LED2_ENABLE_PORT &= ~(1 << LED2_ENABLE_PIN); + #endif + } else { + // enable the power channel, if relevant + #ifdef LED_ENABLE_PIN + #ifdef LED_ON_DELAY + uint8_t led_enable_port_save = LED_ENABLE_PORT; + #endif + + #ifndef LED_ENABLE_PIN_LEVEL_MIN + LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN); + #else + // only enable during part of the ramp + if ((level >= LED_ENABLE_PIN_LEVEL_MIN) + && (level <= LED_ENABLE_PIN_LEVEL_MAX)) + LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN); + else // disable during other parts of the ramp + LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN); + #endif + + // for drivers with a slow regulator chip (eg, boost converter), + // delay before lighting up to prevent flashes + #ifdef LED_ON_DELAY + // only delay if the pin status changed + if (LED_ENABLE_PORT != led_enable_port_save) + delay_4ms(LED_ON_DELAY/4); + #endif + #endif + #ifdef LED2_ENABLE_PIN + #ifdef LED2_ON_DELAY + uint8_t led2_enable_port_save = LED2_ENABLE_PORT; + #endif + + LED2_ENABLE_PORT |= (1 << LED2_ENABLE_PIN); + + // for drivers with a slow regulator chip (eg, boost converter), + // delay before lighting up to prevent flashes + #ifdef LED2_ON_DELAY + // only delay if the pin status changed + if (LED2_ENABLE_PORT != led2_enable_port_save) + delay_4ms(LED2_ON_DELAY/4); + #endif + #endif + + // PWM array index = level - 1 + level --; + + #if PWM_CHANNELS >= 1 + PWM1_LVL = PWM_GET(pwm1_levels, level); + #endif + #if PWM_CHANNELS >= 2 + PWM2_LVL = PWM_GET(pwm2_levels, level); + #endif + #if PWM_CHANNELS >= 3 + PWM3_LVL = PWM_GET(pwm3_levels, level); + #endif + + #ifdef USE_DYN_PWM + uint16_t top = PWM_GET(pwm_tops, level); + #if defined(PWM1_CNT) && defined(PWM1_PHASE_SYNC) + // wait to ensure compare match won't be missed + // (causes visible flickering when missed, because the counter + // goes all the way to 65535 before returning) + // (see attiny1634 reference manual page 103 for a warning about + // the timing of changing the TOP value (section 12.8.4)) + // (but don't wait when turning on from zero, because + // it'll reset the phase below anyway) + // to be safe, allow at least 32 cycles to update TOP + while(actual_level && (PWM1_CNT > (top - 32))) {} + #endif + // pulse frequency modulation, a.k.a. dynamic PWM + PWM1_TOP = top; + #endif // ifdef USE_DYN_PWM + #if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_ON) + // force reset phase when turning on from zero + // (because otherwise the initial response is inconsistent) + if (! actual_level) { + PWM1_CNT = 0; + #if defined(PWM2_CNT) && defined(PWM2_PHASE_RESET_ON) + PWM2_CNT = 0; + #endif + #if defined(PWM3_CNT) && defined(PWM3_PHASE_RESET_ON) + PWM3_CNT = 0; + #endif + } + #endif + } + #ifdef USE_DYNAMIC_UNDERCLOCKING + auto_clock_speed(); + #endif +} +#endif + + +#ifdef USE_SET_LEVEL_GRADUALLY +inline void set_level_gradually(uint8_t lvl) { + gradual_target = lvl; +} + + +// call this every frame or every few frames to change brightness very smoothly +void gradual_tick() { + uint8_t gt = gradual_target; + if (gt < actual_level) gt = actual_level - 1; + else if (gt > actual_level) gt = actual_level + 1; + + // call the relevant hardware-specific function + GradualTickFuncPtr gradual_tick_func = channels[channel_mode].gradual_tick; + bool done = gradual_tick_func(gt - 1); + + if (done) { + uint8_t orig = gradual_target; + set_level(gt); + gradual_target = orig; + } +} +#endif // ifdef USE_SET_LEVEL_GRADUALLY + + +#endif // ifdef USE_RAMPING + diff --git a/fsm/ramping.h b/fsm/ramping.h new file mode 100644 index 0000000..c4b7d48 --- /dev/null +++ b/fsm/ramping.h @@ -0,0 +1,167 @@ +// fsm-ramping.h: Ramping functions for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#ifdef USE_RAMPING + +// actual_level: last ramp level set by set_level() +uint8_t actual_level = 0; +// the level used before actual +uint8_t prev_level = 0; + +void set_level(uint8_t level); +//void set_level_smooth(uint8_t level); +void set_level_zero(); // implement this in a hwdef + +#ifdef USE_SET_LEVEL_GRADUALLY +// adjust brightness very smoothly +uint8_t gradual_target; +inline void set_level_gradually(uint8_t lvl); +void gradual_tick(); + +// reduce repetition with macros +#define GRADUAL_TICK_SETUP() \ + PWM_DATATYPE target; + +// tick to a specific value +#define GRADUAL_ADJUST_SIMPLE(TARGET,PWM) \ + if (PWM < TARGET) PWM ++; \ + else if (PWM > TARGET) PWM --; + +// tick to a specific value, except when immediate 0 to 255 is needed +#define GRADUAL_ADJUST_STACKED(TARGET,PWM,TOP) \ + if ( ((PWM == 0) && (TARGET == TOP)) \ + || ((PWM == TOP) && (TARGET == 0))) \ + PWM = TARGET; \ + else GRADUAL_ADJUST_SIMPLE(TARGET,PWM) + +// tick the top layer of the stack +#define GRADUAL_ADJUST_1CH(TABLE,PWM) \ + target = PWM_GET(TABLE, gt); \ + if (PWM < target) PWM ++; \ + else if (PWM > target) PWM --; + +// tick a base level of the stack +// (with support for special DD FET behavior +// like "low=0, high=255" --> "low=255, high=254") +#define GRADUAL_ADJUST(TABLE,PWM,TOP) \ + target = PWM_GET(TABLE, gt); \ + if ((gt < actual_level) \ + && (PWM == 0) \ + && (target == TOP)) PWM = TOP; \ + else \ + if (PWM < target) PWM ++; \ + else if (PWM > target) PWM --; + +#endif // ifdef USE_SET_LEVEL_GRADUALLY + +// auto-detect the data type for PWM tables +// FIXME: PWM bits and data type should be per PWM table +// FIXME: this whole thing is a mess and should be removed +#ifndef PWM1_BITS + #define PWM1_BITS 8 + #define PWM1_TOP 255 + #define STACKED_PWM_TOP 255 +#endif +#if PWM_BITS <= 8 + #define STACKED_PWM_DATATYPE uint8_t + #define PWM_DATATYPE uint8_t + #define PWM_DATATYPE2 uint16_t + #ifndef PWM_TOP + #define PWM_TOP 255 + #endif + #define STACKED_PWM_TOP 255 + #ifndef PWM_GET + #define PWM_GET(x,y) pgm_read_byte(x+y) + #endif +#else + #define STACKED_PWM_DATATYPE uint16_t + #define PWM_DATATYPE uint16_t + #ifndef PWM_DATATYPE2 + #define PWM_DATATYPE2 uint32_t + #endif + #ifndef PWM_TOP + #define PWM_TOP 1023 // 10 bits by default + #endif + #ifndef STACKED_PWM_TOP + #define STACKED_PWM_TOP 1023 + #endif + // pointer plus 2*y bytes + //#define PWM_GET(x,y) pgm_read_word(x+(2*y)) + // nope, the compiler was already doing the math correctly + #ifndef PWM_GET + #define PWM_GET(x,y) pgm_read_word(x+y) + #endif +#endif +#define PWM_GET8(x,y) pgm_read_byte(x+y) +#define PWM_GET16(x,y) pgm_read_word(x+y) + +// use UI-defined ramp tables if they exist +#ifdef PWM1_LEVELS +PROGMEM const PWM1_DATATYPE pwm1_levels[] = { PWM1_LEVELS }; +#endif +#ifdef PWM2_LEVELS +PROGMEM const PWM2_DATATYPE pwm2_levels[] = { PWM2_LEVELS }; +#endif +#ifdef PWM3_LEVELS +PROGMEM const PWM3_DATATYPE pwm3_levels[] = { PWM3_LEVELS }; +#endif +#ifdef PWM4_LEVELS +PROGMEM const PWM4_DATATYPE pwm4_levels[] = { PWM4_LEVELS }; +#endif +#ifdef PWM5_LEVELS +PROGMEM const PWM5_DATATYPE pwm5_levels[] = { PWM5_LEVELS }; +#endif + +// convenience defs for 1 LED with stacked channels +// FIXME: remove this, use pwm1/2/3 instead +#ifdef LOW_PWM_LEVELS +PROGMEM const PWM_DATATYPE low_pwm_levels[] = { LOW_PWM_LEVELS }; +#endif +#ifdef MED_PWM_LEVELS +PROGMEM const PWM_DATATYPE med_pwm_levels[] = { MED_PWM_LEVELS }; +#endif +#ifdef HIGH_PWM_LEVELS +PROGMEM const PWM_DATATYPE high_pwm_levels[] = { HIGH_PWM_LEVELS }; +#endif + +// 2 channel CCT blending ramp +#ifdef BLEND_PWM_LEVELS +// FIXME: remove this, use pwm1/2/3 instead +PROGMEM const PWM_DATATYPE blend_pwm_levels[] = { BLEND_PWM_LEVELS }; +#endif + + +// pulse frequency modulation, a.k.a. dynamic PWM +// (different ceiling / frequency at each ramp level) +// FIXME: dynamic PWM should be a per-channel option, not global +#ifdef PWM_TOPS +PROGMEM const PWM_DATATYPE pwm_tops[] = { PWM_TOPS }; +#endif + +// FIXME: jump start should be per channel / channel mode +#ifdef USE_JUMP_START + #ifndef JUMP_START_TIME + #define JUMP_START_TIME 8 // in ms, should be 4, 8, or 12 + #endif + #ifndef DEFAULT_JUMP_START_LEVEL + #define DEFAULT_JUMP_START_LEVEL 10 + #endif + #ifdef USE_CFG + #define JUMP_START_LEVEL cfg.jump_start_level + #else + #define JUMP_START_LEVEL jump_start_level + uint8_t jump_start_level = DEFAULT_JUMP_START_LEVEL; + #endif +#endif + +// RAMP_SIZE / MAX_LVL +// cfg-*.h should define RAMP_SIZE +//#define RAMP_SIZE (sizeof(stacked_pwm1_levels)/sizeof(STACKED_PWM_DATATYPE)) +#define MAX_LEVEL RAMP_SIZE + + +#endif // ifdef USE_RAMPING + diff --git a/fsm/random.c b/fsm/random.c new file mode 100644 index 0000000..91fd929 --- /dev/null +++ b/fsm/random.c @@ -0,0 +1,16 @@ +// fsm-random.c: Random number generator for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#ifdef USE_PSEUDO_RAND +uint8_t pseudo_rand() { + static uint16_t offset = 1024; + // loop from 1024 to 4095 + offset = ((offset + 1) & 0x0fff) | 0x0400; + pseudo_rand_seed += 0b01010101; // 85 + return pgm_read_byte(offset) + pseudo_rand_seed; +} +#endif + diff --git a/fsm/random.h b/fsm/random.h new file mode 100644 index 0000000..49aa0cf --- /dev/null +++ b/fsm/random.h @@ -0,0 +1,12 @@ +// fsm-random.h: Random number generator for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#ifdef USE_PSEUDO_RAND +uint8_t pseudo_rand(); +// TODO: test without "volatile", in case it's not needed +volatile uint8_t pseudo_rand_seed = 0; +#endif + diff --git a/fsm/spaghetti-monster.h b/fsm/spaghetti-monster.h new file mode 100644 index 0000000..77431f8 --- /dev/null +++ b/fsm/spaghetti-monster.h @@ -0,0 +1,75 @@ +// spaghetti-monster.h: UI toolkit / microkernel for e-switch flashlights. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +/* + * SpaghettiMonster: Generic foundation code for e-switch flashlights. + * Other possible names: + * - FSM + * - RoundTable + * - Mostly Harmless + * - ... + */ + +#include "tk-attiny.h" + +#include +#include + +// include project definitions to help with recognizing symbols +#include "fsm-events.h" +#include "fsm-states.h" +#include "fsm-adc.h" +#include "fsm-wdt.h" +#include "fsm-pcint.h" +#include "fsm-standby.h" +#include "fsm-channels.h" +#include "fsm-ramping.h" +#include "fsm-random.h" +#ifdef USE_EEPROM +#include "fsm-eeprom.h" +#endif +#include "fsm-misc.h" +#include "fsm-main.h" + +#if defined(USE_DELAY_MS) || defined(USE_DELAY_4MS) || defined(USE_DELAY_ZERO) || defined(USE_DEBUG_BLINK) +#define OWN_DELAY +#include "tk-delay.h" +#endif + +#ifdef USE_DEBUG_BLINK +#define DEBUG_FLASH PWM1_LVL = 64; delay_4ms(2); PWM1_LVL = 0; +void debug_blink(uint8_t num) { + for(; num>0; num--) { + PWM1_LVL = 32; + delay_4ms(100/4); + PWM1_LVL = 0; + delay_4ms(100/4); + } +} +#endif + +// Define these in your SpaghettiMonster recipe +// boot-time tasks +void setup(); +// single loop iteration, runs continuously +void loop(); + +// include executable functions too, for easier compiling +#include "fsm-states.c" +#include "fsm-events.c" +#include "fsm-adc.c" +#include "fsm-wdt.c" +#include "fsm-pcint.c" +#include "fsm-standby.c" +#include "fsm-channels.c" +#include "fsm-ramping.c" +#include "fsm-random.c" +#ifdef USE_EEPROM +#include "fsm-eeprom.c" +#endif +#include "fsm-misc.c" +#include "fsm-main.c" + diff --git a/fsm/spaghetti-monster.txt b/fsm/spaghetti-monster.txt new file mode 100644 index 0000000..434e1bc --- /dev/null +++ b/fsm/spaghetti-monster.txt @@ -0,0 +1,325 @@ +Spaghetti Monster: A UI toolkit library for flashlights +------------------------------------------------------- + +This toolkit takes care of most of the obnoxious parts of dealing with +tiny embedded chips and flashlight hardware, leaving you to focus on the +interface and user-visible features. + +For a quick start, look at the example UIs provided to see how things +are done. They are probably the most useful reference. However, other +details can be found here or in the FSM source code. + + +Why is it called Spaghetti Monster? + + This toolkit is a finite state machine, or FSM. Another thing FSM + stands for is Flying Spaghetti Monster. Source code tends to weave + into intricate knots like spaghetti, called spaghetti code, + particularly when the code isn't using appropriate abstractions for + the task it implements. + + Prior e-switch light code had a tendency to get pretty spaghetti-like, + and it made the code difficult to write, understand, and modify. So I + started from scratch and logically separated the hardware details from + the UI. This effectively put the spaghetti monster in a box, put it + on a leash, to make it behave and stay out of the way while we focus + on the user interface. + + Also, it's just kind of a fun name. :) + + +General concept: + + Spaghetti Monster (FSM) implements a stack-based finite state machine + with an event-handling system. + + Each FSM program should have a setup() function, a loop() function, + and at least one State: + + - The setup() function runs once each time power is connected. + + - The loop() function is called repeatedly whenever the system is + otherwise idle. Put your long-running tasks here, preferably with + consideration taken to allow for cooperative multitasking. + + - The States on the stack will be called whenever an event happens. + States are called in top-to-bottom order until a state returns an + "EVENT_HANDLED" signal. Only do quick tasks here. + + +Finite State Machine: + + Each "State" is simply a callback function which handles events. It + should return EVENT_HANDLED for each event type it does something + with, or EVENT_NOT_HANDLED otherwise. + + Transitions between states typically involve mapping an Event to a new + State, such as this: + + // 3 clicks: go to strobe modes + else if (event == EV_3clicks) { + set_state(strobe_state, 0); + return EVENT_HANDLED; + } + + It is strongly recommended that your State functions never do anything + which takes more than a few milliseconds... and certainly not longer + than 16ms. If you do this, the pending events may pile up to the + point where new events get thrown away. So, do only quick tasks in + the event handler, and do your longer-running tasks in the loop() + function instead. Preferably with precautions taken to allow for + cooperative multitasking. + + If your State function takes longer than one WDT tick (16ms) once in a + while, the system won't break. Several events can be queued. But be + sure not to do it very often. + + Several state management functions are provided: + + - set_state(new_state, arg): Replace the current state on the stack. + Send 'arg' to the new state for its init event. + + - push_state(new_state, arg): Add a new state to the stack, leaving + the current state below it. Send 'arg' to the new state for its + init event. + + - pop_state(): Get rid of (and return) the top-most state. Re-enter + the state below. + + +Event types: + + Event types are defined in fsm-events.h. You may want to adjust these + to fit your program, but the defaults are: + + State transitions: + + - EV_enter_state: Sent to each new State once when it goes onto + the stack. The 'arg' is whatever you define it to be. + + - EV_leave_state: Sent to a state immediately before it is removed + from the stack. + + - EV_reenter_state: If a State gets pushed on top of this one, and + then it pops off, a re-enter Event happens. This should handle + things like consuming the return value of a nested input handler + State. + + Time passing: + + - EV_tick: This happens once per clock tick, which is 16ms or + 62.5Hz by default. The 'arg' is the number of ticks since + entering the state. When 'arg' exceeds 65535, it wraps around + to 32768. + + - EV_sleep_tick: This happens every 0.5s during standby, if + enabled at compile time. The 'arg' is the number of ticks since + entering the state. When 'arg' exceeds 65535, it wraps around + to 32768. + + LVP and thermal regulation: + + - EV_voltage_low: Sent whenever the input power drops below the + VOLTAGE_LOW threshold. Minimum of VOLTAGE_WARNING_SECONDS + between events. + + - EV_temperature_high: Sent whenever the MCU's projected temperature + is higher than therm_ceil. Minimum of one second between events. + The 'arg' indicates how far the temperature exceeds the limit. + + - EV_temperature_low: Sent whenever the MCU's projected temperature + is lower than (therm_ceil - THERMAL_WINDOW_SIZE). Minimum of + one second between events. The 'arg' indicates how far the + temperature exceeds the limit. + + Button presses: + + Button events can be referred to either by pre-defined symbols, or + by teasing out the flags manually. The structure of a button + event is as follows: + + - Bit 7: 1 for button events, 0 otherwise. + + - Bit 6: 1 for a "timeout" event (signals the end of a + sequence), or 0 otherwise. + + - Bit 5: 1 for a "hold" event, 0 otherwise. This flag is only + necessary because, without it, it would be impossible to + distinguish between "click, click, timeout" and "click, hold, + release". + + - Bit 4: 1 if button is currently pressed, 0 otherwise. Button + release events look just like button press events, except this + is not set. + + - Bits 0,1,2,3: Counter for how many clicks there have been. + The first click is 1, second is 2, and it goes up to 15 clicks + in a row. Clicks after 15 are coded as 15. + + The pre-defined button event symbols are like the following: + + - EV_click1_press: The user pressed the button, but no time has + passed since then. + + - EV_click1_release: The user pressed and released the button, + but no time has passed since then. + + - EV_click1_complete: The user clicked the e-switch, released + it, and enough time passed that no more clicks were detected. + (a.k.a. EV_1click) + + - EV_click1_hold: The user pressed the button, and continued + holding it long enough to count as a "hold" event. This event + is sent once per timer tick as long as the button is held, and + the 'arg' value indicates how many timer ticks since the + button state went from 'press' to 'hold'. + + - EV_click1_hold_release: The button was released at the end of + a "hold" event. This is the end of the input sequence, + because no timeout period is used after a hold. + + It's worth noting that a "hold" event can only happen at the + end of an input sequence, and the sequence will reset to empty + after the hold is released. + + If the user pressed the button more than once, events follow the + same pattern. These are the same as above, except with a full + short-press and release first. + + - EV_click2_press + - EV_click2_release + - EV_click2_complete (a.k.a. EV_2clicks) + - EV_click2_hold + - EV_click2_hold_release + + Each of the above patterns continues up to 15 clicks. + + To match entire categories of events, use the bitmasks provided. + For example, to match button events where the button is down or + the button is up, the code would look like this: + + if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { + // button is down (can be a press event or a hold event) + } + else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { + // button was just released + } + + In theory, you could also define your own arbitrary event types, and + emit() them as necessary, and handle them in State functions the same + as any other event. + + +Cooperative multitasking: + + Since we don't have true preemptive multitasking, the best we can do + is cooperative multitasking. In practice, this means: + + - Declare global variables as volatile if they can be changed by an + event handler. This keeps the compiler from caching the value and + causing incorrect behavior. + + - Don't put long-running tasks into State functions. Each State + will get called at least once every 16ms for a clock tick, so they + should not run for longer than 16ms. + + - Put long-running tasks into loop() instead. + + - For long delay() calls, use nice_delay_ms(). This allows the MCU + to process events while we wait. It also automatically aborts if + it detects a state change, and returns a different value. + + In many cases, it shouldn't be necessary to do anything more than + this, but sometimes it will also be a good idea to check the + return value and abort the current task: + + if (! nice_delay_ms(mydelay)) break; + + - In general, try to do small amounts of work and then return + control to other parts of the program. Keep doing small amounts + and yielding until a task is done, instead of trying to do it all + at once. + + +Persistent data in EEPROM: + + To save data which lasts after a battery change, use the eeprom + functions. Define an eeprom style (or two) at the top, define how + many bytes to allocate, and then use the relevant functions as + appropriate. + + - USE_EEPROM / USE_EEPROM_WL: Enable the eeprom-related functions. + With "WL", it uses wear-levelling. Without, it does not. Note: + Wear levelling is not necessarily better -- it uses more ROM, and + it writes more bytes per save(). So, use it only for a few bytes + which change frequently -- not for many bytes or infrequent + changes. + + - EEPROM_BYTES N / EEPROM_WL_BYTES N: Allocate N bytes for the + eeprom data. + + - load_eeprom() / load_eeprom_wl(): Load the stored data into the + eeprom[] or eeprom_wl[] arrays. + Returns 1 if data was found, 0 otherwise. + + - save_eeprom() / save_eeprom_wl(): Save the eeprom[] or eeprom_wl[] + array data to persistent storage. The WL version erases all old + values and writes new ones in a different part of the eeprom + space. The non-WL version updates values in place, and does not + overwrite values which didn't change. + + Note that all interrupts will be disabled during eeprom operations. + + +Useful #defines: + + A variety of things can be #defined before including + spaghetti-monster.h in your program. This allows you to tweak the + behavior and set options to fit your needs: + + - FSM_something_LAYOUT: Select a driver type from tk-attiny.h. This + controls how many power channels there are, which pins they're on, + and what other driver features are available. + + - USE_LVP: Enable low-voltage protection. + + - VOLTAGE_LOW: What voltage should LVP trigger at? Defaults to 29 (2.9V). + + - VOLTAGE_FUDGE_FACTOR: Add this much to the voltage measurements, + to compensate for voltage drop across the reverse-polarity + diode. + + - VOLTAGE_WARNING_SECONDS: How long to wait between LVP events. + + - USE_THERMAL_REGULATION: Enable thermal regulation + + - DEFAULT_THERM_CEIL: Set the temperature limit to use by default + when the user hasn't configured anything. + + - USE_RAMPING: Enable smooth ramping helpers. + + - RAMP_LENGTH: Pick a pre-defined ramp by length. Defined sizes + are 50, 75, and 150 levels. + + - USE_DELAY_4MS, USE_DELAY_MS, USE_DELAY_ZERO: Enable the delay_4ms, + delay_ms(), and delay_zero() functions. Useful for timing-related + activities. + + - HOLD_TIMEOUT: How many clock ticks before a "press" event becomes + a "hold" event? + + - RELEASE_TIMEOUT: How many clock ticks before a "release" event + becomes a "click" event? Basically, the maximum time between + clicks in a double-click or triple-click. + + - USE_BATTCHECK: Enable the battcheck function. Also define one of + the following to select a display style: + + - BATTCHECK_VpT: Volts, pause, tenths. + - BATTCHECK_4bars: Blink up to 4 times. + - BATTCHECK_6bars: Blink up to 6 times. + - BATTCHECK_8bars: Blink up to 8 times. + + - ... and many others. Will try to document them over time, but + they can be found by searching for pretty much anything in + all-caps in the fsm-*.[ch] files. diff --git a/fsm/standby.c b/fsm/standby.c new file mode 100644 index 0000000..5def07c --- /dev/null +++ b/fsm/standby.c @@ -0,0 +1,105 @@ +// fsm-standby.c: standby mode functions for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +#include "fsm-adc.h" +#include "fsm-wdt.h" +#include "fsm-pcint.h" + +// low-power standby mode used while off but power still connected +#define standby_mode sleep_until_eswitch_pressed +void sleep_until_eswitch_pressed() +{ + #ifdef TICK_DURING_STANDBY + WDT_slow(); + #else + WDT_off(); + #endif + + ADC_off(); + + // make sure switch isn't currently pressed + while (button_is_pressed()) {} + empty_event_sequence(); // cancel pending input on suspend + + PCINT_on(); // wake on e-switch event + + #ifdef TICK_DURING_STANDBY + // detect which type of event caused a wake-up + irq_adc = 0; + irq_wdt = 0; + irq_pcint = 0; + while (go_to_standby) { + #else + go_to_standby = 0; + #endif + + // configure sleep mode + #ifdef TICK_DURING_STANDBY + // needs a special sleep mode during measurements + if (adc_active_now) adc_sleep_mode(); + else + #endif + set_sleep_mode(SLEEP_MODE_PWR_DOWN); + + sleep_enable(); + #ifdef BODCR // only do this on MCUs which support it + sleep_bod_disable(); + #endif + sleep_cpu(); // wait here + + // something happened; wake up + sleep_disable(); + + #ifdef TICK_DURING_STANDBY + // determine what woke us up... + if (irq_pcint) { // button pressed; wake up + go_to_standby = 0; + } + if (irq_adc) { // ADC done measuring + #ifndef USE_LOWPASS_WHILE_ASLEEP + adc_reset = 1; // don't lowpass while asleep + #endif + adc_deferred_enable = 1; + adc_deferred(); + //ADC_off(); // takes care of itself + //irq_adc = 0; // takes care of itself + } + if (irq_wdt) { // generate a sleep tick + WDT_inner(); + } + } + #endif + + // don't lowpass immediately after waking + // also, reset thermal history + adc_reset = 2; + + // go back to normal running mode + // PCINT not needed any more, and can cause problems if on + // (occasional reboots on wakeup-by-button-press) + PCINT_off(); + // restore normal awake-mode interrupts + ADC_on(); + WDT_on(); +} + +#ifdef USE_IDLE_MODE +void idle_mode() +{ + // configure sleep mode + set_sleep_mode(SLEEP_MODE_IDLE); + + sleep_enable(); + sleep_cpu(); // wait here + + // something happened; wake up + sleep_disable(); +} +#endif + diff --git a/fsm/standby.h b/fsm/standby.h new file mode 100644 index 0000000..957e2e1 --- /dev/null +++ b/fsm/standby.h @@ -0,0 +1,68 @@ +// fsm-standby.h: standby mode functions for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +// deferred "off" so we won't suspend in a weird state +// (like... during the middle of a strobe pulse) +// set this to nonzero to enter standby mode next time the system is idle +volatile uint8_t go_to_standby = 0; + +#ifdef TICK_DURING_STANDBY +#ifndef STANDBY_TICK_SPEED +#define STANDBY_TICK_SPEED 3 // every 0.128 s +/* + * From the Attiny85 manual: + * 0: 16 ms + * 1: 32 ms + * 2: 64 ms + * 3: 0.128 s + * 4: 0.256 s + * 5: 0.512 s + * 6: 1.0 s + * 7: 2.0 s + * 32: 4.0 s + * 33: 8.0 s + * (other values may have unexpected effects; not sure why the final bit is + * separated from the others, in the "32" position instead of "8", but that's + * how it is) + */ +#endif + +#if (STANDBY_TICK_SPEED == 1) +#define SLEEP_TICKS_PER_SECOND 31 +#define SLEEP_TICKS_PER_MINUTE 1800 + +#elif (STANDBY_TICK_SPEED == 2) +#define SLEEP_TICKS_PER_SECOND 16 +#define SLEEP_TICKS_PER_MINUTE 900 + +#elif (STANDBY_TICK_SPEED == 3) +#define SLEEP_TICKS_PER_SECOND 8 +#define SLEEP_TICKS_PER_MINUTE 450 + +#elif (STANDBY_TICK_SPEED == 4) +#define SLEEP_TICKS_PER_SECOND 4 +#define SLEEP_TICKS_PER_MINUTE 225 + +#elif (STANDBY_TICK_SPEED == 5) +#define SLEEP_TICKS_PER_SECOND 2 +#define SLEEP_TICKS_PER_MINUTE 113 + +#elif (STANDBY_TICK_SPEED == 6) +#define SLEEP_TICKS_PER_SECOND 1 +#define SLEEP_TICKS_PER_MINUTE 57 + +#endif +#endif + +#define standby_mode sleep_until_eswitch_pressed +void sleep_until_eswitch_pressed(); + +#ifdef USE_IDLE_MODE +// stops processing until next click or timer tick +// (I think) +void idle_mode(); +#endif + diff --git a/fsm/states.c b/fsm/states.c new file mode 100644 index 0000000..4b94ce9 --- /dev/null +++ b/fsm/states.c @@ -0,0 +1,105 @@ +// fsm-states.c: State-handling functions for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "fsm-states.h" +#include "fsm-adc.h" + +// TODO: if callback doesn't handle current event, +// pass event to next state on stack? +// Callback return values: +// 0: event handled normally +// 1: event not handled +// 255: error (not sure what this would even mean though, or what difference it would make) +// TODO: function to call stacked callbacks until one returns "handled" + +void _set_state(StatePtr new_state, uint16_t arg, + Event exit_event, Event enter_event) { + // call old state-exit hook (don't use stack) + if (current_state != NULL) current_state(exit_event, arg); + // set new state + current_state = new_state; + // call new state-enter hook (don't use stack) + if (new_state != NULL) current_state(enter_event, arg); + + // since state changed, stop any animation in progress + interrupt_nice_delays(); +} + +int8_t push_state(StatePtr new_state, uint16_t arg) { + if (state_stack_len < STATE_STACK_SIZE) { + // TODO: call old state's exit hook? + // new hook for non-exit recursion into child? + state_stack[state_stack_len] = new_state; + state_stack_len ++; + // FIXME: use EV_stacked_state? + _set_state(new_state, arg, EV_leave_state, EV_enter_state); + return state_stack_len; + } else { + // TODO: um... how is a flashlight supposed to handle a recursion depth error? + return -1; + } +} + +StatePtr pop_state() { + // TODO: how to handle pop from empty stack? + StatePtr old_state = NULL; + StatePtr new_state = NULL; + if (state_stack_len > 0) { + state_stack_len --; + old_state = state_stack[state_stack_len]; + } + if (state_stack_len > 0) { + new_state = state_stack[state_stack_len-1]; + } + // FIXME: what should 'arg' be? (maybe re-entry should be entry with arg+1?) + _set_state(new_state, 0, EV_leave_state, EV_reenter_state); + return old_state; +} + +uint8_t set_state(StatePtr new_state, uint16_t arg) { + // FIXME: this calls exit/enter hooks it shouldn't + // (for the layer underneath the top) + pop_state(); + return push_state(new_state, arg); +} + +void set_state_deferred(StatePtr new_state, uint16_t arg) { + deferred_state = new_state; + deferred_state_arg = arg; +} + +#ifndef DONT_USE_DEFAULT_STATE +// bottom state on stack +// handles default actions for LVP, thermal regulation, etc +uint8_t default_state(Event event, uint16_t arg) { + if (0) {} // this should get compiled out + + #ifdef USE_LVP + else if (event == EV_voltage_low) { + low_voltage(); + return EVENT_HANDLED; + } + #endif + + #if 0 + #ifdef USE_THERMAL_REGULATION + else if (event == EV_temperature_high) { + high_temperature(); + return 0; + } + + else if (event == EV_temperature_low) { + low_temperature(); + return 0; + } + #endif + #endif + + // event not handled + return EVENT_NOT_HANDLED; +} +#endif + diff --git a/fsm/states.h b/fsm/states.h new file mode 100644 index 0000000..156e6cf --- /dev/null +++ b/fsm/states.h @@ -0,0 +1,37 @@ +// fsm-states.h: State-handling functions for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "fsm-adc.h" + +// typedefs +typedef uint8_t State(Event event, uint16_t arg); +typedef State * StatePtr; + +// top of the stack +volatile StatePtr current_state; + +// stack for states, to allow shared utility states like "input a number" +// and such, which return to the previous state after finishing +#define STATE_STACK_SIZE 8 +StatePtr state_stack[STATE_STACK_SIZE]; +uint8_t state_stack_len = 0; + +void _set_state(StatePtr new_state, uint16_t arg, + Event exit_event, Event enter_event); +int8_t push_state(StatePtr new_state, uint16_t arg); +StatePtr pop_state(); +uint8_t set_state(StatePtr new_state, uint16_t arg); + +// if loop() needs to change state, use this instead of set_state() +// (because this avoids race conditions) +volatile StatePtr deferred_state; +volatile uint16_t deferred_state_arg; +void set_state_deferred(StatePtr new_state, uint16_t arg); + +#ifndef DONT_USE_DEFAULT_STATE +uint8_t default_state(Event event, uint16_t arg); +#endif + diff --git a/fsm/wdt.c b/fsm/wdt.c new file mode 100644 index 0000000..64f006e --- /dev/null +++ b/fsm/wdt.c @@ -0,0 +1,197 @@ +// fsm-wdt.c: WDT (Watch Dog Timer) functions for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +// *** Note for the AVRXMEGA3 (1-Series, eg 816 and 817), the WDT +// is not used for time-based interrupts. A new peripheral, the +// Periodic Interrupt Timer ("PIT") is used for this purpose. + +void WDT_on() +{ + #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) + // interrupt every 16ms + //cli(); // Disable interrupts + wdt_reset(); // Reset the WDT + WDTCR |= (1< 0) {} // make sure the register is ready to be updated + RTC.PITCTRLA = RTC_PERIOD_CYC512_gc | RTC_PITEN_bm; // Period = 16ms, enable the PI Timer + #else + #error Unrecognized MCU type + #endif +} + +#ifdef TICK_DURING_STANDBY +inline void WDT_slow() +{ + #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) + // interrupt slower + //cli(); // Disable interrupts + wdt_reset(); // Reset the WDT + WDTCR |= (1< 0) {} // make sure the register is ready to be updated + RTC.PITCTRLA = (1<<6) | (STANDBY_TICK_SPEED<<3) | RTC_PITEN_bm; // Set period, enable the PI Timer + #else + #error Unrecognized MCU type + #endif +} +#endif + +inline void WDT_off() +{ + #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) + //cli(); // Disable interrupts + wdt_reset(); // Reset the WDT + MCUSR &= ~(1< 0) {} // make sure the register is ready to be updated + RTC.PITCTRLA = 0; // Disable the PI Timer + #else + #error Unrecognized MCU type + #endif +} + +// clock tick -- this runs every 16ms (62.5 fps) +#ifdef AVRXMEGA3 // ATTINY816, 817, etc +ISR(RTC_PIT_vect) { + RTC.PITINTFLAGS = RTC_PI_bm; // clear the PIT interrupt flag +#else +ISR(WDT_vect) { +#endif + irq_wdt = 1; // WDT event happened +} + +void WDT_inner() { + irq_wdt = 0; // WDT event handled; reset flag + + static uint8_t adc_trigger = 0; + + // cache this here to reduce ROM size, because it's volatile + uint16_t ticks_since_last = ticks_since_last_event; + // increment, but loop from max back to half + ticks_since_last = (ticks_since_last + 1) \ + | (ticks_since_last & 0x8000); + // copy back to the original + ticks_since_last_event = ticks_since_last; + + // detect and emit button change events (even during standby) + uint8_t was_pressed = button_last_state; + uint8_t pressed = button_is_pressed(); + if (was_pressed != pressed) { + go_to_standby = 0; + PCINT_inner(pressed); + } + // cache again, in case the value changed + ticks_since_last = ticks_since_last_event; + + #ifdef TICK_DURING_STANDBY + // handle standby mode specially + if (go_to_standby) { + // emit a sleep tick, and process it + emit(EV_sleep_tick, ticks_since_last); + process_emissions(); + + #ifndef USE_SLEEP_LVP + return; // no sleep LVP needed if nothing drains power while off + #else + // stop here, usually... except during the first few seconds asleep, + // and once in a while afterward for sleep LVP + if ((ticks_since_last > (8 * SLEEP_TICKS_PER_SECOND)) + && (0 != (ticks_since_last & 0x0f))) return; + + adc_trigger = 0; // make sure a measurement will happen + adc_active_now = 1; // use ADC noise reduction sleep mode + ADC_on(); // enable ADC voltage measurement functions temporarily + #endif + } + else { // button handling should only happen while awake + #endif + + // if time since last event exceeds timeout, + // append timeout to current event sequence, then + // send event to current state callback + + // callback on each timer tick + if ((current_event & B_FLAGS) == (B_CLICK | B_HOLD | B_PRESS)) { + emit(EV_tick, 0); // override tick counter while holding button + } + else { + emit(EV_tick, ticks_since_last); + } + + // user held button long enough to count as a long click? + if (current_event & B_PRESS) { + // during a "hold", send a hold event each tick, with a timer + if (current_event & B_HOLD) { + emit_current_event(ticks_since_last); + } + // has button been down long enough to become a "hold"? + // (first frame of a "hold" event) + else { + if (ticks_since_last >= HOLD_TIMEOUT) { + ticks_since_last_event = 0; + current_event |= B_HOLD; + emit_current_event(0); + } + } + } + + // event in progress, but button not currently down + else if (current_event) { + // "hold" event just ended + // no timeout required when releasing a long-press + if (current_event & B_HOLD) { + //emit_current_event(ticks_since_last); // should have been emitted by PCINT_inner() + empty_event_sequence(); + } + // end and clear event after release timeout + else if (ticks_since_last >= RELEASE_TIMEOUT) { + current_event |= B_TIMEOUT; + emit_current_event(0); + empty_event_sequence(); + } + } + + #ifdef TICK_DURING_STANDBY + } + #endif + + #if defined(USE_LVP) || defined(USE_THERMAL_REGULATION) + // enable the deferred ADC handler once in a while + if (! adc_trigger) { + ADC_start_measurement(); + adc_deferred_enable = 1; + } + // timing for the ADC handler is every 32 ticks (~2Hz) + adc_trigger = (adc_trigger + 1) & 31; + #endif +} + diff --git a/fsm/wdt.h b/fsm/wdt.h new file mode 100644 index 0000000..abf34c5 --- /dev/null +++ b/fsm/wdt.h @@ -0,0 +1,20 @@ +// fsm-wdt.h: WDT (Watch Dog Timer) functions for SpaghettiMonster. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#define TICKS_PER_SECOND 62 + +void WDT_on(); +inline void WDT_off(); + +volatile uint8_t irq_wdt = 0; // WDT interrupt happened? + +#ifdef TICK_DURING_STANDBY + #if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS) + // measure battery charge while asleep + #define USE_SLEEP_LVP + #endif +#endif + diff --git a/hw/BRANDS b/hw/BRANDS new file mode 100644 index 0000000..059f311 --- /dev/null +++ b/hw/BRANDS @@ -0,0 +1,11 @@ +Vendor / Model IDs for version check function + +Undefined 0000 +Emisar 0100 - 0199 +Noctigon 0200 - 0199 +Lumintop 0300 - 0399 +Fireflies 0400 - 0499 +Mateminco 0500 - 0599 +Sofirn 0600 - 0699 +Wurkkos 0700 - 0799 +gChart 1600 - 1699 diff --git a/hw/fireflies/e01/cfg.h b/hw/fireflies/e01/cfg.h new file mode 100644 index 0000000..07bda87 --- /dev/null +++ b/hw/fireflies/e01/cfg.h @@ -0,0 +1,57 @@ +// Fireflies E01 SST-40 thrower config options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0441" +#include "hwdef-ff-pl47.h" +// ATTINY: 85 + +// disable indicator LED; it's hardwired +#ifdef USE_INDICATOR_LED +#undef USE_INDICATOR_LED +#endif + + +#define RAMP_SIZE 150 + +// driver is a FET + 3x7135, ~413 lm at highest regulated level +// ../../../bin/level_calc.py seventh 2 150 7135 1 12 414 FET 2 10 1930 +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12,13,14,15,15,16,17,18,20,21,22,23,24,26,27,28,30,31,33,34,36,38,39,41,43,45,47,49,51,53,56,58,60,63,65,68,71,74,77,80,83,86,89,93,96,100,103,107,111,115,119,124,128,132,137,142,147,152,157,163,168,174,180,186,192,198,204,211,218,225,232,240,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,7,9,12,14,16,19,22,24,27,30,33,36,39,42,45,48,52,55,58,62,66,69,73,77,81,85,90,94,98,103,107,112,117,122,127,132,137,143,148,154,160,166,172,178,184,191,197,204,211,218,225,232,240,247,255 +#define MAX_1x7135 93 +#define HALFSPEED_LEVEL 14 +#define QUARTERSPEED_LEVEL 7 + +// ceiling is level 130/150 (50% power) +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 + +// 20, 38, 56, 75, [93], 111, 130 (93 is highest regulated) +// (9 / 45 / 116 / 248 / 467 / 742 / 1280 + 2140 lm) +#define RAMP_DISCRETE_FLOOR 20 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~50% power +// 20 56 [93] 130 +#define SIMPLE_UI_FLOOR 20 +#define SIMPLE_UI_CEIL 130 +#define SIMPLE_UI_STEPS 4 + +// thermal regulation parameters +#define MIN_THERM_STEPDOWN 65 // lowest value it'll step down to +#define THERM_FASTER_LEVEL 130 + +#ifndef BLINK_AT_RAMP_CEIL +#define BLINK_AT_RAMP_CEIL +#endif + +// don't do this +#undef BLINK_AT_RAMP_MIDDLE + +#define USE_SMOOTH_STEPS + +// too big, turn off extra features +#undef USE_SOS_MODE +//#undef USE_TACTICAL_MODE diff --git a/hw/fireflies/pl47-219/cfg.h b/hw/fireflies/pl47-219/cfg.h new file mode 100644 index 0000000..11bd5b1 --- /dev/null +++ b/hw/fireflies/pl47-219/cfg.h @@ -0,0 +1,15 @@ +// Fireflies PL47-219B config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as PL47 but with FET modes limited to 67% power +// to avoid destroying the LEDs +#include "cfg-ff-pl47.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0422" + +#undef PWM1_LEVELS +#undef PWM2_LEVELS +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12,13,14,15,16,17,18,19,21,22,23,25,26,27,29,31,32,34,36,38,40,42,44,46,49,51,54,56,59,62,65,68,71,74,78,81,85,89,93,97,101,106,110,115,120,125,130,136,141,147,153,160,166,173,180,187,195,202,210,219,227,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,15,16,17,19,20,22,23,25,27,28,30,31,33,35,37,39,41,43,45,47,50,52,55,57,60,63,65,68,71,74,77,80,83,87,90,93,97,101,105,108,112,116,121,125,129,134,139,143,148,153,159,164,169 diff --git a/hw/fireflies/pl47/cfg.h b/hw/fireflies/pl47/cfg.h new file mode 100644 index 0000000..6436477 --- /dev/null +++ b/hw/fireflies/pl47/cfg.h @@ -0,0 +1,79 @@ +// Fireflies PL47 config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0421" +#include "hwdef-ff-pl47.h" +// ATTINY: 85 + +// the button lights up +#define USE_INDICATOR_LED +// the aux LEDs are in the same place as the main LEDs +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif +//#define USE_INDICATOR_LED_WHILE_RAMPING + +// off mode: high (2) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 2) + +// the "low" mode doesn't work on this light's aux LEDs +// (but it does work on the switch LEDs) +// Fireflies wants to skip aux LED mode 1 (low) +#define INDICATOR_LED_SKIP_LOW + + +#define RAMP_SIZE 150 + +// driver is a FET + 3x7135, ~400 lm at highest regulated level +// ramp copied from Emisar D4S ramp +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12,13,14,15,16,17,18,19,21,22,23,25,26,27,29,31,32,34,36,38,40,42,44,46,49,51,54,56,59,62,65,68,71,74,78,81,85,89,93,97,101,106,110,115,120,125,130,136,141,147,153,160,166,173,180,187,195,202,210,219,227,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,10,11,13,14,16,18,19,21,23,25,27,29,31,34,36,38,41,43,46,48,51,54,57,60,63,66,69,72,76,79,83,87,91,95,99,103,107,112,116,121,126,131,136,141,146,152,158,163,169,175,182,188,195,202,209,216,223,231,239,247,255 +#define MAX_1x7135 83 +#define HALFSPEED_LEVEL 13 +#define QUARTERSPEED_LEVEL 6 + +// ceiling is level 120/150 +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 120 + +// 10, 28, 46, 65, 83, 101, 120 (83 is highest regulated) +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL 120 +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~25% power / ~1000 lm +// 10 34 59 [83] 108 +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL 108 +#define SIMPLE_UI_STEPS 5 + +// ~25 lm to ~300 lm +//#define SIMPLE_UI_FLOOR 30 +//#define SIMPLE_UI_CEIL MAX_1x7135 +// ~50 lm to ~500 lm +//#define SIMPLE_UI_FLOOR 40 +//#define SIMPLE_UI_CEIL 90 + +// thermal regulation parameters +#ifdef MIN_THERM_STEPDOWN +#undef MIN_THERM_STEPDOWN // this should be lower, because 3x7135 instead of 1x7135 +#endif +#define MIN_THERM_STEPDOWN 60 // lowest value it'll step down to +// regulate down faster when the FET is active, slower otherwise +#define THERM_FASTER_LEVEL 135 // throttle back faster when high + +#ifndef BLINK_AT_RAMP_CEIL +#define BLINK_AT_RAMP_CEIL +#endif + +// don't do this +#undef BLINK_AT_RAMP_MIDDLE + +#define USE_SMOOTH_STEPS + +// too big, turn off extra features +#undef USE_SOS_MODE +#undef USE_TACTICAL_MODE diff --git a/hw/fireflies/pl47/hwdef.h b/hw/fireflies/pl47/hwdef.h new file mode 100644 index 0000000..7ce7070 --- /dev/null +++ b/hw/fireflies/pl47/hwdef.h @@ -0,0 +1,26 @@ +// Fireflies PL47 driver layout +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as a D4S, basically, except ... + +// ... the PL47 has aux LEDs on pin 7 +#ifndef AUXLED_PIN +#define AUXLED_PIN PB2 // pin 7 +#endif + +// ... and switch LEDs on pin 3 +#ifndef AUXLED2_PIN +#define AUXLED2_PIN PB4 // pin 3 +#endif + +// ... and slightly different calibration +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V +#endif + +#include "hwdef-emisar-d4s.h" +#undef FSM_EMISAR_D4S_DRIVER +#undef FSM_EMISAR_D4_DRIVER + diff --git a/hw/fireflies/pl47g2/cfg.h b/hw/fireflies/pl47g2/cfg.h new file mode 100644 index 0000000..830d7e8 --- /dev/null +++ b/hw/fireflies/pl47g2/cfg.h @@ -0,0 +1,67 @@ +// Fireflies PL47 G2 config options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0423" +#include "hwdef-ff-pl47.h" +// ATTINY: 85 + +// the button lights up +#define USE_INDICATOR_LED +// the aux LEDs are in the same place as the main LEDs +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + +// off mode: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + + + +#define RAMP_SIZE 150 + +// driver is a FET + 3x7135, ~400 lm at highest regulated level +// ramp copied from Emisar D4S ramp +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12,13,14,15,16,17,18,19,21,22,23,25,26,27,29,31,32,34,36,38,40,42,44,46,49,51,54,56,59,62,65,68,71,74,78,81,85,89,93,97,101,106,110,115,120,125,130,136,141,147,153,160,166,173,180,187,195,202,210,219,227,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,10,11,13,14,16,18,19,21,23,25,27,29,31,34,36,38,41,43,46,48,51,54,57,60,63,66,69,72,76,79,83,87,91,95,99,103,107,112,116,121,126,131,136,141,146,152,158,163,169,175,182,188,195,202,209,216,223,231,239,247,255 +#define MAX_1x7135 83 +#define HALFSPEED_LEVEL 13 +#define QUARTERSPEED_LEVEL 6 + +// ceiling is level 120/150 +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 120 + +// 10, 28, 46, 65, 83, 101, 120 (83 is highest regulated) +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL 120 +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~25% power / ~1000 lm +// 10 34 59 [83] 108 +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL 108 +#define SIMPLE_UI_STEPS 5 + +// thermal regulation parameters +#ifdef MIN_THERM_STEPDOWN +#undef MIN_THERM_STEPDOWN // this should be lower, because 3x7135 instead of 1x7135 +#endif +#define MIN_THERM_STEPDOWN 60 // lowest value it'll step down to +// regulate down faster when the FET is active, slower otherwise +#define THERM_FASTER_LEVEL 135 // throttle back faster when high + +#ifndef BLINK_AT_RAMP_CEIL +#define BLINK_AT_RAMP_CEIL +#endif + +// don't do this +#undef BLINK_AT_RAMP_MIDDLE + +#define USE_SMOOTH_STEPS + +// too big, turn off extra features +#undef USE_SOS_MODE +#undef USE_TACTICAL_MODE diff --git a/hw/fireflies/rot66-219/cfg.h b/hw/fireflies/rot66-219/cfg.h new file mode 100644 index 0000000..4dad4e2 --- /dev/null +++ b/hw/fireflies/rot66-219/cfg.h @@ -0,0 +1,26 @@ +// Fireflies ROT66-219 (7x7135) config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as regular ROT66, but calibrated for Nichia 219B with 7x7135 chips +#include "cfg-ff-rot66.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0412" + +// driver is a FET+N+1, +// where N=6 for the 219b version +// calculated to get transition points at level 65 and 115 +// first 65 steps same as regular ROT66 +// remaining steps from this command: +// level_calc.py ninth 3 150 7135 1 1.4 110.1 7135 14 1 1058.5 FET 1 10 4500 +#undef PWM2_LEVELS +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,17,18,20,21,23,25,27,29,31,33,36,38,41,43,46,49,52,55,58,62,65,69,73,77,81,86,90,95,100,105,111,116,122,128,135,141,148,155,163,170,178,187,195,204,214,224,234,244,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#undef PWM3_LEVELS +#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,12,16,21,25,30,35,40,45,50,56,62,68,74,81,87,94,101,109,116,124,133,141,150,159,168,178,188,198,209,220,231,243,255 +#undef MAX_Nx7135 +#define MAX_Nx7135 115 + +// regulate down faster when the FET is active, slower otherwise +#undef THERM_FASTER_LEVEL +#define THERM_FASTER_LEVEL 130 // throttle back faster when high diff --git a/hw/fireflies/rot66/cfg.h b/hw/fireflies/rot66/cfg.h new file mode 100644 index 0000000..752ddbb --- /dev/null +++ b/hw/fireflies/rot66/cfg.h @@ -0,0 +1,65 @@ +// Fireflies ROT66 (14x7135) config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0411" +#include "hwdef-ff-rot66.h" + +// the button lights up +#define USE_INDICATOR_LED +// the button is visible while main LEDs are on +#define USE_INDICATOR_LED_WHILE_RAMPING + +// off mode: high (2) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 2) + +#define RAMP_SIZE 150 + +// driver is a FET+N+1, +// where N=6 for the 219b version, +// or N=13 for the XP-L HI version (this version) +// calculated to get transition points at level 65 and 125 +// first 65 steps copied from FW3A +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +// remaining steps from this command: +// level_calc.py ninth 3 150 7135 1 1.4 110.1 7135 15 1 1644.16 FET 1 10 4500 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,17,18,19,20,21,22,23,25,26,27,29,30,32,34,35,37,39,41,43,46,48,50,53,55,58,61,64,67,70,73,77,81,84,88,92,97,101,106,110,115,120,126,131,137,143,149,156,162,169,177,184,192,200,208,217,226,235,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,13,21,28,35,43,51,60,68,77,87,96,106,117,127,138,150,161,173,186,199,212,226,240,255 +#define MAX_1x7135 65 +#define MAX_Nx7135 125 +#define HALFSPEED_LEVEL 14 +#define QUARTERSPEED_LEVEL 8 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL MAX_Nx7135 +// safe limit max regulated power +// 20 46 72 98 [125] +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL MAX_Nx7135 +#define SIMPLE_UI_STEPS 5 + +// regulate down faster when the FET is active, slower otherwise +#define THERM_FASTER_LEVEL 130 // throttle back faster when high + +// don't do this +#undef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_CEIL + +// enable extra features +#define USE_SMOOTH_STEPS + +// too big, turn off extra features +#undef USE_MOMENTARY_MODE +#undef USE_TACTICAL_MODE +#undef USE_SOS_MODE +//#undef USE_RAMP_AFTER_MOON_CONFIG +//#undef USE_RAMP_SPEED_CONFIG +//#undef USE_VOLTAGE_CORRECTION +//#undef USE_2C_STYLE_CONFIG +//#undef USE_TACTICAL_STROBE_MODE +//#ifdef USE_LOWPASS_WHILE_ASLEEP +//#undef USE_LOWPASS_WHILE_ASLEEP +//#endif + diff --git a/hw/fireflies/rot66/hwdef.h b/hw/fireflies/rot66/hwdef.h new file mode 100644 index 0000000..3fea9f1 --- /dev/null +++ b/hw/fireflies/rot66/hwdef.h @@ -0,0 +1,24 @@ +// Fireflies ROT66 driver layout +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as a FW3A, basically, except ... + +// ... except the ROT66 has a lighted button +#ifndef AUXLED_PIN +#define AUXLED_PIN PB2 // pin 7 +#endif + +// ... and slightly different calibration +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V +#endif + +#include "hwdef-fw3a.h" + +// ... and no optic nerve +#ifdef VISION_PIN +#undef VISION_PIN +#endif + diff --git a/hw/fireflies/rot66g2/cfg.h b/hw/fireflies/rot66g2/cfg.h new file mode 100644 index 0000000..04aac53 --- /dev/null +++ b/hw/fireflies/rot66g2/cfg.h @@ -0,0 +1,59 @@ +// Fireflies ROT66 G2 config options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "cfg-ff-rot66.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0413" + +// if the "low" mode was disabled, turn it back on +#ifdef INDICATOR_LED_SKIP_LOW +#undef INDICATOR_LED_SKIP_LOW +#endif + +// lockout: blinking (3), off: low (1) +#ifdef INDICATOR_LED_DEFAULT_MODE +#undef INDICATOR_LED_DEFAULT_MODE +#endif +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + +// the button is *not* visible while main LEDs are on +#undef USE_INDICATOR_LED_WHILE_RAMPING + +// ramp shape is different than original ROT66 +// 1x7135: 150 lm +// Nx7135: 1200 lm +// FET: 4500 lm +// ../../../bin/level_calc.py 7.0 3 150 7135 1 4 180.16 7135 8 1 1374.48 FET 1 10 4000 +// (plus some manual tweaks) +#undef PWM1_LEVELS +#undef PWM2_LEVELS +#undef PWM3_LEVELS +#define PWM1_LEVELS 1,1,2,3,3,4,4,5,6,7,7,8,9,10,11,12,16,17,18,19,20,21,22,24,26,28,30,32,35,37,40,42,45,48,51,55,58,62,65,69,74,78,83,87,92,98,103,109,115,121,128,134,142,149,157,165,173,182,191,201,211,221,232,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,11,13,14,16,18,20,22,24,26,28,30,33,35,38,41,43,46,49,52,55,59,62,66,69,73,77,81,85,89,94,98,103,108,113,118,124,129,135,141,147,153,160,166,173,180,188,195,203,211,219,228,237,246,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,11,17,23,29,36,42,49,56,63,71,78,86,94,102,111,119,128,137,147,156,166,176,187,197,208,219,231,243,255 + +#undef MAX_1x7135 +#undef MAX_Nx7135 +#define MAX_1x7135 65 // ~150 lm +#define MAX_Nx7135 120 // ~1200 m +#undef HALFSPEED_LEVEL +#undef QUARTERSPEED_LEVEL +#define HALFSPEED_LEVEL 17 +#define QUARTERSPEED_LEVEL 6 + +// higher floor than default, and stop at highest regulated level +#define RAMP_SMOOTH_FLOOR 1 // ~0.3 lm +#define RAMP_SMOOTH_CEIL MAX_Nx7135 // ~1200 lm +// 10, 28, 46, [65], 83, 101, [120] +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL MAX_Nx7135 +#define RAMP_DISCRETE_STEPS 7 + +// safe limit max regulated power +// 10 37 65 92 [120] +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL MAX_Nx7135 +#define SIMPLE_UI_STEPS 5 + diff --git a/hw/gchart/fet1-t1616/cfg.h b/hw/gchart/fet1-t1616/cfg.h new file mode 100644 index 0000000..4082fca --- /dev/null +++ b/hw/gchart/fet1-t1616/cfg.h @@ -0,0 +1,63 @@ +// gChart's custom FET+1 driver config options for Anduril +// Copyright (C) 2020-2023 gchart, Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "1618" // Golden Ratio... because I can +#include "hwdef-gchart-fet1-t1616.h" +// ATTINY: 1616 + +// the button lights up +#define USE_INDICATOR_LED +// the button is visible while main LEDs are on +#define USE_INDICATOR_LED_WHILE_RAMPING +// off mode: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + +#define RAMP_SIZE 150 + +// 7135 at 90/150 +// level_calc.py 5.7895 2 150 7135 0 0.1 125.25 FET 1 10 1200 --pwm dyn:61:4096:255:2.5 --clock 5:11:2.0 +// (with heavy manual tweaks up to ~15/150) +#define PWM1_LEVELS 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 23, 24, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 35, 36, 37, 38, 40, 41, 43, 45, 47, 50, 53, 56, 60, 63, 67, 71, 75, 79, 84, 89, 94, 99,104,110,116,122,129,136,143,150,158,166,174,183,192,202,211,222,232,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 5, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 29, 31, 34, 37, 39, 42, 45, 48, 51, 54, 58, 61, 65, 68, 72, 76, 80, 84, 88, 93, 97,102,107,112,117,122,127,133,139,145,151,157,163,170,177,183,191,198,205,213,221,229,238,246,255 +#define PWM_TOPS 4095,2893,3917,2806,3252,2703,2684,2660,2640,2370,3000,2900,2630,2549,2246,2193,2030,1961,1889,1716,1642,1569,1497,1428,1290,1232,1176,1122,1070,976,932,890,849,779,745,685,656,605,579,536,514,476,457,424,407,379,364,340,327,314,302,291,280,276,266,262,257,253,253,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#define MAX_1x7135 90 +#define MIN_THERM_STEPDOWN 60 +#define HALFSPEED_LEVEL 11 +#define QUARTERSPEED_LEVEL 5 +#define DEFAULT_LEVEL 50 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 +// 10 30 50 70 [90] 110 130 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL 130 +#define RAMP_DISCRETE_STEPS 7 + +// 10 30 50 70 [90] 110 130 +#define SIMPLE_UI_FLOOR 10 +#define SIMPLE_UI_CEIL 130 +#define SIMPLE_UI_STEPS 7 + +// stop panicking at ~50% power +#define THERM_FASTER_LEVEL 130 // throttle back faster when high + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the aux LEDs by default +//#define DEFAULT_BLINK_CHANNEL CM_AUX + +// the default of 26 looks a bit rough, so increase it to make it smoother +#define CANDLE_AMPLITUDE 40 + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// enable factory reset on 13H without loosening tailcap (required) +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/gchart/fet1-t1616/hwdef.h b/hw/gchart/fet1-t1616/hwdef.h new file mode 100644 index 0000000..2d2b7a6 --- /dev/null +++ b/hw/gchart/fet1-t1616/hwdef.h @@ -0,0 +1,134 @@ +// 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 +*/ + +#define ATTINY 1616 +#include + +// 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 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 +#define AUXLED_PIN PIN3_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 = 0b00000010; + // 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 + 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; + PORTA.PIN7CTRL = PORT_PULLUPEN_bm; + + //PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // FET channel + //PORTB.PIN1CTRL = PORT_PULLUPEN_bm; // 7135 channel + PORTB.PIN2CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // switch + //PORTB.PIN3CTRL = PORT_PULLUPEN_bm; // Aux LED + PORTB.PIN4CTRL = PORT_PULLUPEN_bm; + PORTB.PIN5CTRL = PORT_PULLUPEN_bm; + + 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/hw/hank/emisar-2ch-fet/cfg.h b/hw/hank/emisar-2ch-fet/cfg.h new file mode 100644 index 0000000..4686483 --- /dev/null +++ b/hw/hank/emisar-2ch-fet/cfg.h @@ -0,0 +1,113 @@ +// Emisar 2-channel generic (plus FET) config options for Anduril (based on Noctigon K9.3) +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0136" +#include "hwdef-emisar-2ch-fet.h" +#include "hank-cfg.h" +// ATTINY: 1634 + +// 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 +// TODO: the whole "indicator LED" thing needs to be refactored into +// "aux LED(s)" and "button LED(s)" since they work a bit differently +// enabling this option breaks the button LED on D4v2.5 +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + +// channel modes... +// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO +// enable max brightness out of the box +#define DEFAULT_CHANNEL_MODE CM_BOTH + +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_BOTH + +#define POLICE_COLOR_STROBE_CH1 CM_CH1 +#define POLICE_COLOR_STROBE_CH2 CM_CH2 + +// how much to increase total brightness at middle tint +// (0 = 100% brightness, 64 = 200% brightness) +#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it + + +// channel 1 / main LEDs (w/ DD FET) +// output: unknown, 1750 lm? +// FET: unknown, 3000 lm? +// channel 2 +// output: unknown, 1750 lm? +// combined: 4000 to 5000 lm? +#define RAMP_SIZE 150 + +// linear+FET ramp: maxreg at 140/150 +// level_calc.py 5.01 2 150 7135 1 0.1 1924 FET 1 10 2600 --pwm dyn:68:4096:255:3 --clock 8:16:0 +// linear segment +#define PWM1_LEVELS 1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7,7,8,8,8,9,9,9,10,10,11,12,13,13,14,15,16,17,17,18,19,20,21,22,23,25,26,27,28,30,31,33,34,36,37,39,41,43,44,46,48,50,53,55,57,60,62,65,67,70,73,76,79,82,85,88,92,95,99,103,106,110,114,119,123,127,132,137,142,147,152,157,162,168,174,179,185,192,198,204,211,218,225,232,240,247,255,255,255,255,255,255,255,255,255,255,255 +// DD FET segment +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,68,93,118,144,171,198,226,255 +// PWM TOPS values +#define PWM3_LEVELS 4096,3831,3570,3314,3063,2817,2577,2342,2115,1894,1682,1477,1281,2237,2005,1784,1576,1380,1196,1111,1026,1442,1348,1215,1091,976,871,774,1031,942,860,784,714,650,591,538,652,602,555,513,473,437,403,372,343,398,370,345,322,299,278,311,292,273,255,278,261,245,263,247,232,246,231,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 + +// linear-only ramp +// level_calc.py 3.11 1 150 7135 1 0.1 1750 --pwm dyn:64:4096:255:3 --clock 8:16:0 +#define PWM4_LEVELS 1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,5,5,6,6,6,7,7,7,8,8,8,9,9,9,10,10,10,10,10,10,11,11,11,11,11,11,11,12,12,12,12,12,12,13,13,13,14,14,14,15,15,16,17,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,33,34,35,37,38,39,41,42,44,45,47,48,50,52,53,55,57,59,61,63,65,67,69,71,73,75,77,79,82,84,86,89,91,94,97,99,102,105,107,110,113,116,119,122,125,128,132,135,138,141,145,148,152,156,159,163,167,170,174,178,182,186,190,195,199,203,208,212,217,221,226,230,235,240,245,250,255 +// PWM_TOPS values for linear-only ramp +#define PWM5_LEVELS 4096,3681,3247,2794,2328,1856,2937,2393,1860,2690,2273,1875,2281,1959,1658,1893,1646,1774,1569,1381,1466,1309,1166,1224,1104,996,1033,942,858,882,810,746,687,634,586,604,561,522,487,454,425,397,409,385,362,341,321,302,311,295,279,286,271,257,263,250,255,258,246,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 + +#define DEFAULT_LEVEL 75 +#define MAX_1x7135 140 +#define MAX_Nx7135 MAX_1x7135 +#define HALFSPEED_LEVEL 16 +#define QUARTERSPEED_LEVEL 8 + +#define RAMP_SMOOTH_FLOOR 10 // level 1 is unreliable (?) +#define RAMP_SMOOTH_CEIL 140 +// 10, 31, 53, [75], 96, 118, [140] +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit highest regulated power (no FET or turbo) +// 10, 42, [75], 107, 140 +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~1500 lm +#define THERM_FASTER_LEVEL 130 +#define MIN_THERM_STEPDOWN 65 // should be above highest dyn_pwm level + +#define USE_POLICE_COLOR_STROBE_MODE +#undef TACTICAL_LEVELS +#define TACTICAL_LEVELS 140,30,(RAMP_SIZE+3) // high, low, police strobe + +// use the brightest setting for strobe +#define STROBE_BRIGHTNESS MAX_LEVEL +// slow down party strobe; this driver can't pulse for 1ms or less +#define PARTY_STROBE_ONTIME 2 +// TODO: change speed per channel mode +// (the FET is really fast, but the regulator is not) +//#undef PARTY_STROBE_ONTIME + +// the default of 26 looks a bit flat, so increase it +#define CANDLE_AMPLITUDE 33 + +// the power regulator is a bit slow, so push it harder for a quick response from off +#define DEFAULT_JUMP_START_LEVEL 40 +#define BLINK_BRIGHTNESS 45 +#define BLINK_ONCE_TIME 12 // longer blink, since main LEDs are slow + +#define THERM_CAL_OFFSET 5 + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + diff --git a/hw/hank/emisar-2ch-fet/hwdef.c b/hw/hank/emisar-2ch-fet/hwdef.c new file mode 100644 index 0000000..caf579d --- /dev/null +++ b/hw/hank/emisar-2ch-fet/hwdef.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/hw/hank/emisar-2ch-fet/hwdef.h b/hw/hank/emisar-2ch-fet/hwdef.h new file mode 100644 index 0000000..d756a0d --- /dev/null +++ b/hw/hank/emisar-2ch-fet/hwdef.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 + +#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<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/hw/hank/emisar-2ch/hwdef.h b/hw/hank/emisar-2ch/hwdef.h new file mode 100644 index 0000000..99e4945 --- /dev/null +++ b/hw/hank/emisar-2ch/hwdef.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 + +#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< + +#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/hw/hank/emisar-d1s/cfg.h b/hw/hank/emisar-d1s/cfg.h new file mode 100644 index 0000000..8b70a5d --- /dev/null +++ b/hw/hank/emisar-d1s/cfg.h @@ -0,0 +1,23 @@ +// Emisar D1S config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as Emisar D4, mostly +#include "hwdef-emisar-d4.h" +#include "cfg-emisar-d4.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0122" + +// safe limit ~50% power +#undef SIMPLE_UI_CEIL +#define SIMPLE_UI_CEIL 120 + +// stop panicking at ~90% power or ~1200 lm (D1S has a good power-to-thermal-mass ratio) +#ifdef THERM_FASTER_LEVEL +#undef THERM_FASTER_LEVEL +#endif +#define THERM_FASTER_LEVEL 144 // throttle back faster when high + +// too big, turn off extra features +//#undef USE_TACTICAL_MODE diff --git a/hw/hank/emisar-d1v2-7135-fet/cfg.h b/hw/hank/emisar-d1v2-7135-fet/cfg.h new file mode 100644 index 0000000..4022ba6 --- /dev/null +++ b/hw/hank/emisar-d1v2-7135-fet/cfg.h @@ -0,0 +1,32 @@ +// Emisar D1v2 (7135+FET) config options for Anduril +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// ATTINY: 1634 +// same as Emisar D4v2, mostly +// (was only made for a short time, not many people have one) +#include "cfg-emisar-d4v2.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0123" + +// some models use a simple button LED, others use RGB... +// ... so include support for both +#define USE_BUTTON_LED +// the aux LEDs are in the button, so use them while main LEDs are on +// (early short run had no button LEDs at all, later run uses linear+FET instead, +// so it's unlikely that anyone needs this, but it doesn't hurt anything) +#define USE_AUX_RGB_LEDS +#define USE_AUX_RGB_LEDS_WHILE_ON 25 +#define USE_INDICATOR_LED_WHILE_RAMPING + +// safe limit ~50% power +#undef SIMPLE_UI_CEIL +#define SIMPLE_UI_CEIL 120 + +// stop panicking at ~75% power or ~1000 lm (D1 has a decent power-to-thermal-mass ratio) +#ifdef THERM_FASTER_LEVEL +#undef THERM_FASTER_LEVEL +#endif +#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high + diff --git a/hw/hank/emisar-d1v2-linear-fet/cfg.h b/hw/hank/emisar-d1v2-linear-fet/cfg.h new file mode 100644 index 0000000..28c57f8 --- /dev/null +++ b/hw/hank/emisar-d1v2-linear-fet/cfg.h @@ -0,0 +1,30 @@ +// Emisar D1v2 (linear+FET) config options for Anduril +// (2022 re-issue / update of old D1) +// Copyright (C) 2022-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// ATTINY: 1634 +// similar to a Noctigon KR4, sort of +#include "cfg-noctigon-kr4.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0124" + +// some models use a simple button LED, others use RGB... +// ... so include support for both +#define USE_BUTTON_LED +// the aux LEDs are in the button, so use them while main LEDs are on +#define USE_AUX_RGB_LEDS +#define USE_AUX_RGB_LEDS_WHILE_ON 25 +#define USE_INDICATOR_LED_WHILE_RAMPING + +// safe limit: max regulated power +#undef SIMPLE_UI_CEIL +#define SIMPLE_UI_CEIL MAX_1x7135 + +// stop panicking at ~75% power or ~1000 lm (D1 has a decent power-to-thermal-mass ratio) +#ifdef THERM_FASTER_LEVEL +#undef THERM_FASTER_LEVEL +#endif +#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high + diff --git a/hw/hank/emisar-d1v2-nofet/cfg.h b/hw/hank/emisar-d1v2-nofet/cfg.h new file mode 100644 index 0000000..7f5bcc4 --- /dev/null +++ b/hw/hank/emisar-d1v2-nofet/cfg.h @@ -0,0 +1,24 @@ +// Emisar D1v2 (linear only, no DDFET) config options for Anduril +// (2022 re-issue / update of old D1) +// Copyright (C) 2022-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// ATTINY: 1634 +// similar to a Noctigon KR4, sort of +#include "cfg-noctigon-kr4-nofet.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0125" + +// some models use a simple button LED, others use RGB... +// ... so include support for both +#define USE_BUTTON_LED +// the aux LEDs are in the button, so use them while main LEDs are on +#define USE_AUX_RGB_LEDS +#define USE_AUX_RGB_LEDS_WHILE_ON 25 +#define USE_INDICATOR_LED_WHILE_RAMPING + +// safe limit: same as regular ramp +#undef SIMPLE_UI_CEIL +#define SIMPLE_UI_CEIL RAMP_SMOOTH_CEIL + diff --git a/hw/hank/emisar-d4-219/cfg.h b/hw/hank/emisar-d4-219/cfg.h new file mode 100644 index 0000000..65649e3 --- /dev/null +++ b/hw/hank/emisar-d4-219/cfg.h @@ -0,0 +1,17 @@ +// Emisar D4-219C config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as D4 but with FET modes limited to 80% power +// to avoid destroying the LEDs +#include "cfg-emisar-d4.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0112" + +// don't turn off first channel at turbo level +#undef PWM1_LEVELS +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +// 65% FET power +#undef PWM2_LEVELS +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,15,16,17,18,19,21,22,23,25,26,27,28,30,32,33,34,36,38,39,41,42,44,46,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,78,80,82,84,87,90,92,94,97,99,102,104,108,110,113,116,119,121,125,127,130,134,136,140,143,146,149,153,156,159,163,166 diff --git a/hw/hank/emisar-d4/cfg.h b/hw/hank/emisar-d4/cfg.h new file mode 100644 index 0000000..4b3ae5a --- /dev/null +++ b/hw/hank/emisar-d4/cfg.h @@ -0,0 +1,48 @@ +// Emisar D4 config options for Anduril +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0111" +#include "hwdef-emisar-d4.h" +#include "hank-cfg.h" +// ATTINY: 85 + +#define RAMP_SIZE 150 + +// ../../bin/level_calc.py 1 65 7135 1 0.8 150 +// ... mixed with this: +// ../../bin/level_calc.py 2 150 7135 4 0.33 150 FET 1 10 1500 +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255 + +#define MAX_1x7135 65 +#define DEFAULT_LEVEL 65 +#define HALFSPEED_LEVEL 15 +#define QUARTERSPEED_LEVEL 6 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 120 +// 10, 28, 46, [65], 83, 101, 120 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~20% power +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL 100 +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~30% power or ~1200 lm +#define THERM_FASTER_LEVEL 105 + +// don't blink during ramp +#undef BLINK_AT_RAMP_MIDDLE + +// enable extra features +#define USE_SMOOTH_STEPS + +// too big, turn off extra features +//#undef USE_TACTICAL_MODE +#undef USE_SOS_MODE + diff --git a/hw/hank/emisar-d4/hwdef.c b/hw/hank/emisar-d4/hwdef.c new file mode 100644 index 0000000..972f682 --- /dev/null +++ b/hw/hank/emisar-d4/hwdef.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/hw/hank/emisar-d4/hwdef.h b/hw/hank/emisar-d4/hwdef.h new file mode 100644 index 0000000..7be700a --- /dev/null +++ b/hw/hank/emisar-d4/hwdef.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 + +#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/hw/hank/emisar-d4k-3ch/cfg.h b/hw/hank/emisar-d4k-3ch/cfg.h new file mode 100644 index 0000000..c39ac01 --- /dev/null +++ b/hw/hank/emisar-d4k-3ch/cfg.h @@ -0,0 +1,106 @@ +// Emisar D4K 3-channel config options for Anduril +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0151" +#include "hwdef-emisar-d4k-3ch.h" +#include "hank-cfg.h" +// ATTINY: 1634 + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// turn on the aux LEDs while main LEDs are on +// (in case there's a RGB button) +#define USE_AUX_RGB_LEDS_WHILE_ON 40 +#define USE_INDICATOR_LED_WHILE_RAMPING + +// channel modes... +// CM_MAIN2, CM_LED3, CM_LED4, CM_ALL, +// CM_BLEND34A, CM_BLEND34B, CM_HSV, CM_AUTO3 +#define DEFAULT_CHANNEL_MODE CM_ALL + +#define FACTORY_RESET_WARN_CHANNEL CM_LED4 +#define FACTORY_RESET_SUCCESS_CHANNEL CM_MAIN2 + +#define CONFIG_WAITING_CHANNEL CM_LED3 +#define CONFIG_BLINK_CHANNEL CM_ALL + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_MAIN2 + +// LEDs 3 and 4 make a nice police strobe +#define POLICE_COLOR_STROBE_CH1 CM_LED3 +#define POLICE_COLOR_STROBE_CH2 CM_LED4 +// aux red + aux blue are the correct colors, but are dim +//#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +//#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// how much to increase total brightness at middle tint +// (0 = 100% brightness, 64 = 200% brightness) +#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it + +// main 2 LEDs +// output: unknown, 2000 lm? +// LED 3 / 4 +// output: unknown, 1000 lm each? +#define RAMP_SIZE 150 +// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits) +// level_calc.py 5.01 1 150 7135 0 0.2 2000 --pwm 32640 +// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) +#define PWM1_LEVELS 0,1,2,3,4,5,6,7,9,10,12,14,17,19,22,25,28,32,36,41,45,50,56,62,69,76,84,92,101,110,121,132,143,156,169,184,199,215,232,251,270,291,313,336,360,386,414,442,473,505,539,574,612,651,693,736,782,829,880,932,987,1045,1105,1168,1233,1302,1374,1449,1527,1608,1693,1781,1873,1969,2068,2172,2279,2391,2507,2628,2753,2883,3018,3158,3303,3454,3609,3771,3938,4111,4289,4475,4666,4864,5068,5280,5498,5724,5957,6197,6445,6701,6965,7237,7518,7808,8106,8413,8730,9056,9392,9737,10093,10459,10835,11223,11621,12031,12452,12884,13329,13786,14255,14737,15232,15741,16262,16798,17347,17911,18489,19082,19691,20314,20954,21609,22281,22969,23674,24397,25137,25895,26671,27465,28279,29111,29963,30835,31727,32640 + +#define MIN_THERM_STEPDOWN 50 +#define DEFAULT_LEVEL 70 +#define MAX_1x7135 70 +// always run at 1/4th speed, because 4 kHz PWM is enough for this circuit +// and speed changes make a big visible bump +#define HALFSPEED_LEVEL 255 +#define QUARTERSPEED_LEVEL 255 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 +// 10, 30, 50, [70], 90, 110, 130 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// 10 40 [70] 100 130 +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~1500 lm +#define THERM_FASTER_LEVEL 130 + +#define USE_POLICE_COLOR_STROBE_MODE +#undef TACTICAL_LEVELS +#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe + +// use the brightest setting for strobe +#define STROBE_BRIGHTNESS MAX_LEVEL +// slow down party strobe; this driver can't pulse for 1ms or less +#define PARTY_STROBE_ONTIME 2 +// #define STROBE_OFF_LEVEL 1 // nope, this makes strobe blurry +// bike strobe needs a longer pulse too? +//#define BIKE_STROBE_ONTIME 8 + +// the default of 26 looks a bit flat, so increase it +#define CANDLE_AMPLITUDE 33 + +// the power regulator is a bit slow, so push it harder for a quick response from off +#define DEFAULT_JUMP_START_LEVEL 21 +#define BLINK_BRIGHTNESS 50 +#define BLINK_ONCE_TIME 12 // longer blink, since main LEDs are slow + +#define THERM_CAL_OFFSET 5 + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// for consistency with KR4 (not otherwise necessary though) +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/hank/emisar-d4k-3ch/hwdef.c b/hw/hank/emisar-d4k-3ch/hwdef.c new file mode 100644 index 0000000..e35af08 --- /dev/null +++ b/hw/hank/emisar-d4k-3ch/hwdef.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/hw/hank/emisar-d4k-3ch/hwdef.h b/hw/hank/emisar-d4k-3ch/hwdef.h new file mode 100644 index 0000000..2e83fbe --- /dev/null +++ b/hw/hank/emisar-d4k-3ch/hwdef.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 + +#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< (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/hw/hank/emisar-d4sv2/hwdef.h b/hw/hank/emisar-d4sv2/hwdef.h new file mode 100644 index 0000000..11d1abb --- /dev/null +++ b/hw/hank/emisar-d4sv2/hwdef.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 + +#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< (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/hw/hank/emisar-d4v2/cfg.h b/hw/hank/emisar-d4v2/cfg.h new file mode 100644 index 0000000..0f0bb17 --- /dev/null +++ b/hw/hank/emisar-d4v2/cfg.h @@ -0,0 +1,64 @@ +// Emisar D4v2 config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0113" +#include "hwdef-emisar-d4v2.h" +#include "hank-cfg.h" +// ATTINY: 1634 + +#define RAMP_SIZE 150 + +// 7135 at 75/150 +// level_calc.py 5.7895 2 150 7135 1 0.1 130 FET 1 10 3000 --pwm dyn:74:4096:255:3 +// (with some manual tweaks) +// non-zero part of FET channel calculated with: +// level_calc.py 3 1 75 7135 2 500 3000 +#define PWM1_LEVELS 1,1,2,3,3,4,5,6,7,8,9,11,12,13,15,16,18,19,21,23,26,27,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,71,74,76,78,80,82,85,87,90,93,96,100,103,107,112,116,122,127,133,140,147,154,163,171,182,192,203,215,228,241,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,5,7,9,11,13,15,17,19,21,23,25,27,29,32,34,36,39,41,43,46,49,51,54,57,59,62,65,68,71,74,77,80,83,86,90,93,96,100,103,107,110,114,117,121,125,129,133,137,141,145,149,153,157,162,166,170,175,179,184,189,193,198,203,208,213,218,223,228,233,239,244,249,255 +#define PWM_TOPS 4095,2701,3200,3586,2518,2778,2834,2795,2705,2587,2455,2582,2412,2247,2256,2091,2062,1907,1860,1802,1737,1605,1542,1477,1412,1347,1284,1222,1162,1105,1050,997,946,898,853,810,768,730,693,658,625,594,564,536,503,485,462,439,418,398,384,366,353,340,327,319,307,298,292,284,280,273,269,266,263,260,258,256,256,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 + +#define MAX_1x7135 75 +#define DEFAULT_LEVEL 50 +#define MIN_THERM_STEPDOWN 70 // should be above highest dyn_pwm level +#define HALFSPEED_LEVEL 20 +#define QUARTERSPEED_LEVEL 5 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 +// 20, 38, 56, [75], 93, 111, 130 +#define RAMP_DISCRETE_FLOOR 20 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// 20, 40, 60, 80, 100 +#define SIMPLE_UI_FLOOR 20 +#define SIMPLE_UI_CEIL 100 +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~25% power or ~1000 lm +#define THERM_FASTER_LEVEL 110 + +#define THERM_CAL_OFFSET 5 + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_MAIN + +// use aux red + aux blue for police strobe +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_STROBE_USES_AUX +#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// the default of 26 looks a bit rough, so increase it to make it smoother +#define CANDLE_AMPLITUDE 33 + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + diff --git a/hw/hank/emisar-d4v2/hwdef.c b/hw/hank/emisar-d4v2/hwdef.c new file mode 100644 index 0000000..026b30d --- /dev/null +++ b/hw/hank/emisar-d4v2/hwdef.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/hw/hank/emisar-d4v2/hwdef.h b/hw/hank/emisar-d4v2/hwdef.h new file mode 100644 index 0000000..96c57a9 --- /dev/null +++ b/hw/hank/emisar-d4v2/hwdef.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 + +#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< end of dynamic PWM range +#define HALFSPEED_LEVEL 12 +#define QUARTERSPEED_LEVEL 4 +#endif + +// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits) +// level_calc.py 5.01 1 150 7135 0 0.2 2000 --pwm 32640 +// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) +#define PWM1_LEVELS 0,1,2,3,4,5,6,7,9,10,12,14,17,19,22,25,28,32,36,41,45,50,56,62,69,76,84,92,101,110,121,132,143,156,169,184,199,215,232,251,270,291,313,336,360,386,414,442,473,505,539,574,612,651,693,736,782,829,880,932,987,1045,1105,1168,1233,1302,1374,1449,1527,1608,1693,1781,1873,1969,2068,2172,2279,2391,2507,2628,2753,2883,3018,3158,3303,3454,3609,3771,3938,4111,4289,4475,4666,4864,5068,5280,5498,5724,5957,6197,6445,6701,6965,7237,7518,7808,8106,8413,8730,9056,9392,9737,10093,10459,10835,11223,11621,12031,12452,12884,13329,13786,14255,14737,15232,15741,16262,16798,17347,17911,18489,19082,19691,20314,20954,21609,22281,22969,23674,24397,25137,25895,26671,27465,28279,29111,29963,30835,31727,32640 +#define MIN_THERM_STEPDOWN 50 +#define DEFAULT_LEVEL 70 +#define MAX_1x7135 150 +// always run at 1/4th speed, because 4 kHz PWM is enough for this circuit +// and speed changes make a big visible bump +#define HALFSPEED_LEVEL 255 +#define QUARTERSPEED_LEVEL 255 + +#define RAMP_SMOOTH_FLOOR 1 // low levels may be unreliable +#define RAMP_SMOOTH_CEIL 130 +// 10, 30, 50, [70], 90, 110, 130 +// Nichia B35 model: (0.56), 1.4, 8.4, 34.5, [102], 250, 500, 860, (1300) lm +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~75% power +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~70% power or ~600 lm +#define THERM_FASTER_LEVEL 130 + +#define THERM_CAL_OFFSET 5 + +// the power regulator seems to "jump start" the LEDs all on its own, +// so the firmware doesn't have to +// (and unfortunately the power regulator jumps it a bit too hard) +#define DEFAULT_JUMP_START_LEVEL 1 +#define BLINK_BRIGHTNESS 50 +#define BLINK_ONCE_TIME 12 + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_MAIN + +// slow down party strobe; this driver can't pulse for 2ms or less +#define PARTY_STROBE_ONTIME 3 + +// use aux red + aux blue for police strobe +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_STROBE_USES_AUX +#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// the default of 26 looks a bit rough, so increase it to make it smoother +#define CANDLE_AMPLITUDE 33 + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// added for convenience +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/hank/noctigon-dm11-boost/hwdef.c b/hw/hank/noctigon-dm11-boost/hwdef.c new file mode 100644 index 0000000..932323a --- /dev/null +++ b/hw/hank/noctigon-dm11-boost/hwdef.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/hw/hank/noctigon-dm11-boost/hwdef.h b/hw/hank/noctigon-dm11-boost/hwdef.h new file mode 100644 index 0000000..d56a5f5 --- /dev/null +++ b/hw/hank/noctigon-dm11-boost/hwdef.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 + +#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< end of dynamic PWM range + diff --git a/hw/hank/noctigon-dm11-sbt90/cfg.h b/hw/hank/noctigon-dm11-sbt90/cfg.h new file mode 100644 index 0000000..9fac446 --- /dev/null +++ b/hw/hank/noctigon-dm11-sbt90/cfg.h @@ -0,0 +1,46 @@ +// Noctigon DM11-SBT90.2 config options for Anduril +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "cfg-noctigon-kr4.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0274" +// ATTINY: 1634 + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS +// ... and a single LED in the button +#define USE_BUTTON_LED +// don't use aux LEDs while main LED is on +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + +// power channels: +// - linear: 5A? +// - FET: DD + +#undef DEFAULT_LEVEL +#define DEFAULT_LEVEL 70 + +#undef RAMP_SMOOTH_FLOOR +#define RAMP_SMOOTH_FLOOR 10 // low levels may be unreliable +// 10, 30, 50, [70], 90, 110, 130 +#undef RAMP_DISCRETE_FLOOR +#define RAMP_DISCRETE_FLOOR 10 + +// safe limit ~75% power +#undef SIMPLE_UI_CEIL +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL + +// stop panicking at ~70% power or ~600 lm +#undef THERM_FASTER_LEVEL +#define THERM_FASTER_LEVEL 130 + +#undef BLINK_BRIGHTNESS +#define BLINK_BRIGHTNESS 50 + +#undef CANDLE_AMPLITUDE +#define CANDLE_AMPLITUDE 30 + diff --git a/hw/hank/noctigon-dm11/cfg.h b/hw/hank/noctigon-dm11/cfg.h new file mode 100644 index 0000000..cd6bc9d --- /dev/null +++ b/hw/hank/noctigon-dm11/cfg.h @@ -0,0 +1,88 @@ +// Noctigon DM11 config options for Anduril +// Copyright (C) 2021-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0271" +#include "hwdef-noctigon-dm11.h" +#include "hank-cfg.h" +// ATTINY: 1634 + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS +// ... and a single LED in the button +#define USE_BUTTON_LED +// don't use aux LEDs while main LED is on +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + +#define RAMP_SIZE 150 + +// power channels: +// - linear: 5A? +// - FET: DD + +// maxreg at 130, dynamic PWM: level_calc.py 5.01 2 149 7135 1 0.3 1740 FET 1 10 3190 --pwm dyn:64:16384:255 +// (plus one extra level at the beginning for moon) +#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255 +#define PWM_TOPS 16383,16383,11750,14690,9183,12439,13615,13955,13877,13560,13093,12529,13291,12513,12756,12769,11893,11747,12085,11725,11329,11316,10851,10713,10518,10282,10016,9729,9428,9298,8971,8794,8459,8257,8043,7715,7497,7275,7052,6753,6538,6260,5994,5798,5501,5271,5006,4758,4525,4268,4030,3775,3508,3263,3010,2752,2517,2256,1998,1763,1512,1249,994,749,497,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#define MIN_THERM_STEPDOWN 66 // should be above highest dyn_pwm level + +#define MAX_1x7135 130 +#define DEFAULT_LEVEL 70 +#define HALFSPEED_LEVEL 12 +#define QUARTERSPEED_LEVEL 4 + +#define RAMP_SMOOTH_FLOOR 10 // low levels may be unreliable +#define RAMP_SMOOTH_CEIL 130 +// 10, 30, 50, [70], 90, 110, 130 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~75% power +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~70% power or ~600 lm +#define THERM_FASTER_LEVEL 130 + +#define THERM_CAL_OFFSET 5 + + +// the power regulator is a bit slow, so push it harder for a quick response from off +#define DEFAULT_JUMP_START_LEVEL 21 +#define BLINK_BRIGHTNESS 50 +#define BLINK_ONCE_TIME 12 + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// there is usually no lighted button, so +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_MAIN + +// slow down party strobe; this driver can't pulse for 1ms or less +// (only needed on no-FET build) +//#define PARTY_STROBE_ONTIME 2 + +// use aux red + aux blue for police strobe +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_STROBE_USES_AUX +#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// make candle mode wobble more +#define CANDLE_AMPLITUDE 30 + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// added for convenience +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/hank/noctigon-dm11/hwdef.h b/hw/hank/noctigon-dm11/hwdef.h new file mode 100644 index 0000000..b35b27f --- /dev/null +++ b/hw/hank/noctigon-dm11/hwdef.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 + +#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< + +#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< + +#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< (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/hw/hank/noctigon-k1/hwdef.h b/hw/hank/noctigon-k1/hwdef.h new file mode 100644 index 0000000..6467567 --- /dev/null +++ b/hw/hank/noctigon-k1/hwdef.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 + +#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< + +// move the switch to a different pin +#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 rest of the config is the same as the generic Emisar 2ch build +#include "hwdef-emisar-2ch.h" + diff --git a/hw/hank/noctigon-kr4-boost/cfg.h b/hw/hank/noctigon-kr4-boost/cfg.h new file mode 100644 index 0000000..1603acf --- /dev/null +++ b/hw/hank/noctigon-kr4-boost/cfg.h @@ -0,0 +1,16 @@ +// Noctigon KR4 (12V) config options for Anduril +// (and Noctigon KR1) +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// the only things different from dm11-boost are: +// - e-switch is on a different pin (defined in hwdef) +// - different model number +#include "cfg-noctigon-dm11-boost.h" +#include "hwdef-noctigon-kr4-boost.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0216" +#include "hank-cfg.h" +// ATTINY: 1634 + diff --git a/hw/hank/noctigon-kr4-boost/hwdef.h b/hw/hank/noctigon-kr4-boost/hwdef.h new file mode 100644 index 0000000..07c4f4b --- /dev/null +++ b/hw/hank/noctigon-kr4-boost/hwdef.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/hw/hank/noctigon-kr4-nofet/cfg.h b/hw/hank/noctigon-kr4-nofet/cfg.h new file mode 100644 index 0000000..6d49a8b --- /dev/null +++ b/hw/hank/noctigon-kr4-nofet/cfg.h @@ -0,0 +1,66 @@ +// Noctigon KR4 (no DD FET) config options for Anduril +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// (and Noctigon KR1) +// (and Emisar D4v2 E21A, a.k.a. "D4v2.5") +#define HWDEF_C_FILE hwdef-noctigon-kr4-nofet.c +#include "cfg-noctigon-kr4.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0212" +// ATTINY: 1634 + +// brightness w/ SST-20 4000K LEDs: +// 0/1023: 0.35 lm +// 1/1023: 2.56 lm +// max regulated: 1740 lm +#undef PWM_CHANNELS +#define PWM_CHANNELS 1 +#define RAMP_SIZE 150 +// prioritize low lows, at risk of visible ripple +// level_calc.py 5.01 1 149 7135 1 0.3 1740 --pwm dyn:78:16384:255 +#undef PWM1_LEVELS +#define PWM1_LEVELS 0,1,1,1,2,3,3,4,5,6,7,8,9,10,11,13,14,16,17,19,21,23,25,27,29,31,34,36,39,42,44,47,50,53,57,60,63,67,70,74,77,81,85,88,92,96,99,103,107,110,113,117,120,123,126,128,130,133,134,136,137,137,137,137,136,135,133,130,126,122,117,111,104,96,87,76,65,52,38,22,23,25,26,27,28,29,30,32,33,34,36,37,39,40,42,43,45,47,49,51,53,55,57,59,61,63,66,68,70,73,76,78,81,84,87,90,93,96,99,103,106,110,113,117,121,125,129,133,137,142,146,151,155,160,165,170,175,181,186,192,197,203,209,215,222,228,234,241,248,255 +#undef PWM2_LEVELS +#undef PWM_TOPS +#define PWM_TOPS 16383,16383,12404,8140,11462,14700,11041,12947,13795,14111,14124,13946,13641,13248,12791,13418,12808,13057,12385,12428,12358,12209,12000,11746,11459,11147,11158,10793,10708,10576,10173,9998,9800,9585,9527,9278,9023,8901,8634,8486,8216,8053,7881,7615,7440,7261,7009,6832,6656,6422,6196,6031,5819,5615,5419,5190,4973,4803,4571,4386,4179,3955,3745,3549,3340,3145,2940,2729,2513,2312,2109,1903,1697,1491,1286,1070,871,662,459,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#undef DEFAULT_LEVEL +#define DEFAULT_LEVEL 50 +#undef MAX_1x7135 +#define MAX_1x7135 150 + +#undef RAMP_SMOOTH_FLOOR +#undef RAMP_SMOOTH_CEIL +#undef RAMP_DISCRETE_FLOOR +#undef RAMP_DISCRETE_CEIL +#undef RAMP_DISCRETE_STEPS + +#define RAMP_SMOOTH_FLOOR 11 // low levels may be unreliable +#define RAMP_SMOOTH_CEIL 130 +// 11, 30, [50], 70, 90, 110, 130 (plus [150] on turbo) +#define RAMP_DISCRETE_FLOOR 11 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~1000 lm (can sustain 900 lm) +#undef SIMPLE_UI_FLOOR +#undef SIMPLE_UI_CEIL +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL + + +// slow down party strobe; this driver can't pulse for 1ms or less +// (only needed on no-FET build) +#define PARTY_STROBE_ONTIME 2 + +// jump start a bit higher than base driver +#undef DEFAULT_JUMP_START_LEVEL +#define DEFAULT_JUMP_START_LEVEL 25 + +// stop panicking at ~1300 lm +#undef THERM_FASTER_LEVEL +#define THERM_FASTER_LEVEL 140 +#undef MIN_THERM_STEPDOWN +#define MIN_THERM_STEPDOWN 80 // must be > end of dynamic PWM range + diff --git a/hw/hank/noctigon-kr4-nofet/hwdef.c b/hw/hank/noctigon-kr4-nofet/hwdef.c new file mode 100644 index 0000000..0492def --- /dev/null +++ b/hw/hank/noctigon-kr4-nofet/hwdef.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/hw/hank/noctigon-kr4/cfg.h b/hw/hank/noctigon-kr4/cfg.h new file mode 100644 index 0000000..5b24ef5 --- /dev/null +++ b/hw/hank/noctigon-kr4/cfg.h @@ -0,0 +1,89 @@ +// Noctigon KR4 config options for Anduril +// (and Emisar D4v2.5, which uses KR4 driver plus a button LED) +// Copyright (C) 2020-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0211" +#include "hwdef-noctigon-kr4.h" +#include "hank-cfg.h" +// ATTINY: 1634 + +// brightness w/ SST-20 4000K LEDs: +// 0/1023: 0.35 lm +// 1/1023: 2.56 lm +// max regulated: 1740 lm +// FET: ~3700 lm +#define RAMP_SIZE 150 + +// nice low lows, but might have visible ripple on some lights: +// maxreg at 130, dynamic PWM: level_calc.py 5.01 2 149 7135 1 0.3 1740 FET 1 10 3190 --pwm dyn:64:16384:255 +// (plus one extra level at the beginning for moon) +#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255 +#define PWM_TOPS 16383,16383,11750,14690,9183,12439,13615,13955,13877,13560,13093,12529,13291,12513,12756,12769,11893,11747,12085,11725,11329,11316,10851,10713,10518,10282,10016,9729,9428,9298,8971,8794,8459,8257,8043,7715,7497,7275,7052,6753,6538,6260,5994,5798,5501,5271,5006,4758,4525,4268,4030,3775,3508,3263,3010,2752,2517,2256,1998,1763,1512,1249,994,749,497,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#define MIN_THERM_STEPDOWN 66 // should be above highest dyn_pwm level + +// less ripple, but lows are a bit higher than ideal: +// maxreg at 130, dynamic PWM: level_calc.py 5.01 2 149 7135 1 0.3 1740 FET 1 10 3190 --pwm dyn:64:4096:255 +// (plus one extra level at the beginning for moon) +//#define PWM1_LEVELS 0,1,1,1,1,1,1,2,2,2,2,3,3,3,4,4,5,5,6,6,7,8,8,9,10,11,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,32,33,33,34,34,34,34,34,34,33,32,31,30,28,26,24,21,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +//#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255 +//#define PWM_TOPS 4095,4095,3760,3403,3020,2611,2176,3582,3062,2515,1940,3221,2761,2283,2998,2584,3004,2631,2899,2555,2735,2836,2538,2606,2636,2638,2387,2382,2361,2328,2286,2238,2185,2129,2070,2010,1949,1887,1826,1766,1706,1648,1591,1536,1482,1429,1379,1329,1242,1199,1122,1084,1016,953,895,842,791,723,659,602,549,482,422,367,302,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 + +#define MAX_1x7135 130 +#define DEFAULT_LEVEL 50 +#define HALFSPEED_LEVEL 12 +#define QUARTERSPEED_LEVEL 4 + +#define RAMP_SMOOTH_FLOOR 11 // low levels may be unreliable +#define RAMP_SMOOTH_CEIL 130 +// 11 30 [50] 70 90 110 [130] +#define RAMP_DISCRETE_FLOOR 11 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~30% power / ~1300 lm (can sustain 900 lm) +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL 120 +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~1300 lm +#define THERM_FASTER_LEVEL 120 + +#define THERM_CAL_OFFSET 5 + + +// the power regulator is a bit slow, so push it harder for a quick response from off +#define DEFAULT_JUMP_START_LEVEL 21 +#define BLINK_BRIGHTNESS DEFAULT_LEVEL +#define BLINK_ONCE_TIME 12 + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// there is usually no lighted button, so +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_MAIN + +// slow down party strobe; this driver can't pulse for 1ms or less +// (only needed on no-FET build) +//#define PARTY_STROBE_ONTIME 2 + +// use aux red + aux blue for police strobe +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_STROBE_USES_AUX +#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// the default of 26 looks a bit rough, so increase it to make it smoother +#define CANDLE_AMPLITUDE 33 + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// can't reset the normal way because power is connected before the button +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/hank/noctigon-kr4/hwdef.c b/hw/hank/noctigon-kr4/hwdef.c new file mode 100644 index 0000000..884151d --- /dev/null +++ b/hw/hank/noctigon-kr4/hwdef.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/hw/hank/noctigon-kr4/hwdef.h b/hw/hank/noctigon-kr4/hwdef.h new file mode 100644 index 0000000..5570fb7 --- /dev/null +++ b/hw/hank/noctigon-kr4/hwdef.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 + +#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<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<> 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/hw/hank/noctigon-m44/hwdef.h b/hw/hank/noctigon-m44/hwdef.h new file mode 100644 index 0000000..5658c9f --- /dev/null +++ b/hw/hank/noctigon-m44/hwdef.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 + +#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< + +#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/hw/lumintop/fw3a-219/cfg.h b/hw/lumintop/fw3a-219/cfg.h new file mode 100644 index 0000000..0bd5250 --- /dev/null +++ b/hw/lumintop/fw3a-219/cfg.h @@ -0,0 +1,16 @@ +// FW3A-219 config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "cfg-fw3a.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0312" + +#undef PWM1_LEVELS +#undef PWM2_LEVELS +#undef PWM3_LEVELS +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,8,10,13,15,17,19,22,24,26,29,31,34,37,39,42,45,48,51,54,57,60,64,67,70,74,77,81,85,88,92,96,100,104,108,112,116,121,125,130,134,139,143,148,153,158,163,168,173,179,184,189,195,201,206,212,218,224,230,236,243,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,13,19,25,30,36,42,48,54,61,67,73,80,86,93,100,107,114,120,128 + diff --git a/hw/lumintop/fw3a-nofet/cfg.h b/hw/lumintop/fw3a-nofet/cfg.h new file mode 100644 index 0000000..b5d4166 --- /dev/null +++ b/hw/lumintop/fw3a-nofet/cfg.h @@ -0,0 +1,54 @@ +// FW3A with the FET disabled +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "cfg-fw3a.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0313" + +// don't use channel 3 (FET) +#undef PWM_CHANNELS +#undef CH3_PIN +#undef CH3_PWM +#define PWM_CHANNELS 2 + +// reconfigure the ramp +#undef PWM1_LEVELS +#undef PWM2_LEVELS +#undef PWM3_LEVELS +// copied from Emisar D4, mostly +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255 +#undef MAX_1x7135 +#define MAX_1x7135 65 +#undef MAX_Nx7135 +#undef HALFSPEED_LEVEL +#define HALFSPEED_LEVEL 15 +#undef QUARTERSPEED_LEVEL +#define QUARTERSPEED_LEVEL 6 + +#undef RAMP_SMOOTH_FLOOR +#undef RAMP_SMOOTH_CEIL +#undef RAMP_DISCRETE_FLOOR +#undef RAMP_DISCRETE_CEIL +#undef RAMP_DISCRETE_STEPS + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 150 +// 10, 33, 56, 80, 103, 126, 150 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~25% power +// 10 37 65 92 120 +#undef SIMPLE_UI_CEIL +#define SIMPLE_UI_CEIL 120 + +#undef THERM_FASTER_LEVEL +#define THERM_FASTER_LEVEL 130 + +// without the 3rd channel, extra features can fit +#define USE_TACTICAL_MODE + diff --git a/hw/lumintop/fw3a/cfg.h b/hw/lumintop/fw3a/cfg.h new file mode 100644 index 0000000..53c4135 --- /dev/null +++ b/hw/lumintop/fw3a/cfg.h @@ -0,0 +1,54 @@ +// FW3A config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0311" +#include "hwdef-fw3a.h" + +#define RAMP_SIZE 150 + +// ../../bin/level_calc.py 1 65 7135 1 0.8 150 +// ... mixed with this: +// ../../../bin/level_calc.py 3 150 7135 1 0.33 150 7135 1 1 850 FET 1 10 1500 +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,8,10,13,15,17,19,22,24,26,29,31,34,37,39,42,45,48,51,54,57,60,64,67,70,74,77,81,85,88,92,96,100,104,108,112,116,121,125,130,134,139,143,148,153,158,163,168,173,179,184,189,195,201,206,212,218,224,230,236,243,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,19,31,43,55,67,79,91,104,117,130,143,157,170,184,198,212,226,240,255 + +#define DEFAULT_LEVEL 50 +#define MAX_1x7135 65 +#define MAX_Nx7135 130 +#define HALFSPEED_LEVEL 15 +#define QUARTERSPEED_LEVEL 6 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL MAX_Nx7135 +// 10, 30, 50, [70], 90, 110, 130 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~20% power +// 20 40 60 80 100 +#define SIMPLE_UI_FLOOR 20 +#define SIMPLE_UI_CEIL 100 +#define SIMPLE_UI_STEPS 5 + +// stop panicking at about 3A or ~1100 lm, this light is a hotrod +#define THERM_FASTER_LEVEL MAX_Nx7135 + +// don't blink during ramp +#define BLINK_AT_RAMP_CEIL +#undef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_FLOOR + +// enable extra features +#define USE_SMOOTH_STEPS + +// can't reset the normal way because power is connected before the button +#define USE_SOFT_FACTORY_RESET + +// too big, turn off extra features +#undef USE_TACTICAL_MODE +#undef USE_SOS_MODE + diff --git a/hw/lumintop/fw3a/hwdef.c b/hw/lumintop/fw3a/hwdef.c new file mode 100644 index 0000000..b20969d --- /dev/null +++ b/hw/lumintop/fw3a/hwdef.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/hw/lumintop/fw3a/hwdef.h b/hw/lumintop/fw3a/hwdef.h new file mode 100644 index 0000000..519fb97 --- /dev/null +++ b/hw/lumintop/fw3a/hwdef.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 + +#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/hw/lumintop/fw3x-lume1/cfg.h b/hw/lumintop/fw3x-lume1/cfg.h new file mode 100644 index 0000000..184ab8e --- /dev/null +++ b/hw/lumintop/fw3x-lume1/cfg.h @@ -0,0 +1,81 @@ +// lume1 for FW3x config options for Anduril +// Copyright (C) 2020-2023 LoneOceans, Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +/* + * Constant current Buck-Boost + FET driver + * For more information: www.loneoceans.com/labs/ + * Datasheets: + * - 1634: http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8303-8-bit-AVR-Microcontroller-tinyAVR-ATtiny1634_Datasheet.pdf + * - 85: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf + */ + +#define MODEL_NUMBER "0314" +#include "hwdef-fw3x-lume1.h" +// ATTINY: 1634 + +// set this light for 50C thermal ceiling +#undef DEFAULT_THERM_CEIL +#define DEFAULT_THERM_CEIL 50 + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// it has no independent LED in the button unlike emisar d4 +//#define USE_BUTTON_LED + +// the aux LEDs are front-facing, so turn them off while main LEDs are on +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + +// ../../bin/level_calc.py cube 1 149 7135 0 0.5 1000, with 0 appended to the end. +// for FET PWM (PWM2), all values are 0, except for last value of 1023 +// (with max_pwm set to 1023) +#define RAMP_SIZE 150 +//#define PWM1_LEVELS 0,0,0,0,1,1,1,1,2,2,2,3,3,4,4,5,5,6,7,7,8,9,10,11,12,13,14,15,16,17,19,20,22,23,25,26,28,30,32,34,36,38,40,42,45,47,49,52,55,58,60,63,66,70,73,76,80,83,87,91,94,98,102,107,111,115,120,124,129,134,139,144,150,155,160,166,172,178,184,190,196,203,209,216,223,230,237,244,252,259,267,275,283,291,299,308,316,325,334,343,353,362,372,382,392,402,412,423,433,444,455,466,478,489,501,513,525,538,550,563,576,589,602,616,630,644,658,672,687,701,716,731,747,762,778,794,810,827,844,861,878,895,913,930,948,967,985,1004,1023,0 +#define PWM1_LEVELS 1,1,1,1,2,2,2,2,3,3,3,4,4,5,5,6,6,7,8,8,9,10,11,12,13,14,15,16,17,18,20,21,23,24,26,27,29,31,33,35,37,39,41,43,45,48,50,53,56,58,61,64,67,70,74,77,80,84,88,91,95,99,103,108,112,116,121,125,130,135,140,145,150,156,161,167,173,178,184,191,197,203,210,217,223,230,238,245,252,260,268,275,283,292,300,308,317,326,335,344,353,363,372,382,392,402,413,423,434,445,456,467,478,490,502,514,526,538,551,563,576,589,603,616,630,644,658,672,687,702,717,732,747,763,778,794,811,827,844,861,878,895,913,931,949,967,985,1004,1023,0 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1023 +#define DEFAULT_LEVEL 56 +#define MAX_1x7135 149 +// TODO: test if underclocking works on lume1 +//#define HALFSPEED_LEVEL 14 +//#define QUARTERSPEED_LEVEL 5 +// don't slow down at low levels; this isn't that sort of light +// (it needs to stay at full speed for the 10-bit PWM to work) +#ifdef USE_DYNAMIC_UNDERCLOCKING +#undef USE_DYNAMIC_UNDERCLOCKING +#endif + +// the entire ramp is regulated except turbo; don't blink halfway up +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 149 +// turn on BuckBoost from level 1 to 149, but not 150 +// Level 150 is when BuckBoost is off and FET is ON 100% +#define LED_ENABLE_PIN_LEVEL_MIN 1 +#define LED_ENABLE_PIN_LEVEL_MAX 149 +// 10 33 56 79 102 125 [149] +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL 120 +#define SIMPLE_UI_STEPS 5 + +// slow down party strobe; this driver can't pulse for too short a time +#define PARTY_STROBE_ONTIME 4 + +// stop panicking at ~85% regulated power or ~750 lm +#define THERM_FASTER_LEVEL 140 + +#define THERM_CAL_OFFSET 0 // not needed due to external sensor + +// can't reset the normal way because power is connected before the button +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/lumintop/fw3x-lume1/hwdef.c b/hw/lumintop/fw3x-lume1/hwdef.c new file mode 100644 index 0000000..2f31ed0 --- /dev/null +++ b/hw/lumintop/fw3x-lume1/hwdef.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/hw/lumintop/fw3x-lume1/hwdef.h b/hw/lumintop/fw3x-lume1/hwdef.h new file mode 100644 index 0000000..c03248b --- /dev/null +++ b/hw/lumintop/fw3x-lume1/hwdef.h @@ -0,0 +1,202 @@ +// 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 + * 5 PA2 e-switch (PCINT2) + * 6 PA1 Jumper 1 + * 7 PA0 Jumper 2 + * 8 GND GND + * 9 VCC VCC + * 10 PC5 Jumper 3 + * 11 PC4 Jumper 4 + * 12 PC3 RESET + * 13 PC2 External Thermal Sensor (ADC11) + * 14 PC1 SCK + * 15 PC0 N/C + * 16 PB3 FET PWM (PWM1A) + * 17 PB2 MISO + * 18 PB1 MOSI + * 19 PB0 ADC5 Voltage Battery Sense (2:1 divider) + * 20 PA7 BB_Enable + * 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 DD FET control. + */ + +#define ATTINY 1634 +#include + +#define HWDEF_C_FILE hwdef-fw3x-lume1.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 2 // old, remove this + +// Added for Lume1 Buck Boost Driver +#define PWM_BITS 16 // 0 to 1023 at 3.9 kHz, not 0 to 255 at 15.6 kHz +#define PWM_GET PWM_GET16 +#define PWM_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 +#define JUMPER2_PIN PA0 +#define JUMPER3_PIN PC5 +#define JUMPER4_PIN PC4 +*/ + +// 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 +#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC5D +// 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, 0, 1 for ADC5 / PB0 +// divided by ... +// REFS[1:0] = 0, 0 for VCC as analog reference at 2.5V +// other bits reserved +#define ADMUX_VOLTAGE_DIVIDER 0b00000101 +#define ADC_PRSCL 0x06 // clk/64 + +// Raw ADC readings at 4.4V and 2.2V +// calibrate the voltage readout here +// estimated / calculated values are: +// [(Vbatt)*(R2/(R2+R1)) / 2.5V] * 1023 +// R1 = R2 = 100kR +#ifndef ADC_44 +#define ADC_44 900 +#endif +#ifndef ADC_22 +#define ADC_22 450 +#endif + +// Default ADMUX_THERM for Temperature is: 0b10001110 in tk-attiny.h +// REFS[1:0] as 10 for analog reference at internal 1.1Vref +// MUX[3:0] as 1110 for ADC'12' - temperature sensor internal + +// Modified fsm-adc.c to use different ADMUX and ADC_temperature_handler() +// based on USE_EXTERNAL_TEMP_SENSOR +// See line 34 and line 209 +#define USE_EXTERNAL_TEMP_SENSOR +#define ADMUX_THERM_EXTERNAL_SENSOR 0b00001011 // VCC reference (2.5V), Channel PC2 +// Used for Lume1 Driver: MCP9700 - T_Celsius = 100*(VOUT - 0.5V) +// ADC is 2.5V reference, 0 to 1023 +// FIXME: due to floating point, this calculation takes 916 extra bytes +// (should use an integer equivalent instead) +#define EXTERN_TEMP_FORMULA(m) (((m)-205)/4.09) + +// this driver allows for aux LEDs under the optic +#define AUXLED_R_PIN PA5 // pin 2 +#define AUXLED_G_PIN PA4 // pin 3 +#define AUXLED_B_PIN PA3 // pin 4 +#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 + +// For lume1 driver, no SW support for Auxillary Jumpers X1 to X4 yet! +inline void hwdef_setup() { + // 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< + +#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/hw/mateminco/mf01s/cfg.h b/hw/mateminco/mf01s/cfg.h new file mode 100644 index 0000000..de92693 --- /dev/null +++ b/hw/mateminco/mf01s/cfg.h @@ -0,0 +1,61 @@ +// Mateminco MT18S / Astrolux MF01S options for Anduril +// Copyright (C) 2019-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0511" +#include "hwdef-mateminco-mf01s.h" +// ATTINY: 85 + +// the button lights up +#define USE_INDICATOR_LED +// the button is visible while main LEDs are on +//#define USE_INDICATOR_LED_WHILE_RAMPING +// off mode: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + + +#define RAMP_SIZE 150 + +// measured brightness with 4x30Q cells at 4.11V: +// moon: 2.5 lm +// channel 1: 617 lm +// channel 2: 13500 lm +// ../../../bin/level_calc.py seventh 2 150 7135 1 12 717 FET 1 10 13000 +// (with some manual tweaks afterward) +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,7,7,8,9,10,11,12,13,14,15,17,18,19,21,22,24,26,28,30,32,34,36,38,41,44,46,49,52,55,59,62,66,70,74,78,83,87,92,97,102,108,114,120,126,133,139,147,154,162,170,178,187,196,206,215,226,236,248,255,235,255,240,255,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,3,3,4,4,5,6,7,8,9,10,11,12,13,14,16,17,19,20,22,23,25,26,28,30,31,33,35,37,39,41,43,45,47,49,52,54,57,59,62,65,67,70,73,76,80,83,86,90,93,97,101,105,109,113,117,122,126,131,135,140,145,151,156,161,167,173,179,185,191,197,204,211,218,225,232,239,247,255 + +#define MAX_1x7135 70 // ~626 lm +#define HALFSPEED_LEVEL 23 +#define QUARTERSPEED_LEVEL 6 + +#define RAMP_SMOOTH_FLOOR 1 // ~2.5 lm +#define RAMP_SMOOTH_CEIL 120 // ~5400 lm +// 20, 36, 53, [70], 86, 103, 120 +#define RAMP_DISCRETE_FLOOR 20 // 35 lm +#define RAMP_DISCRETE_CEIL 120 // ~5400 lm +#define RAMP_DISCRETE_STEPS 7 // 35, 108, 280, 626, 1500, 2930, 5400 lm + +// safe limit ~25% power +// 18 35 52 [70] 87 105 +#define SIMPLE_UI_FLOOR 18 +#define SIMPLE_UI_CEIL 105 +#define SIMPLE_UI_STEPS 6 + +#define THERM_FASTER_LEVEL 125 // throttle back faster when high (>6000 lm) +#define THERM_HARD_TURBO_DROP // this light is massively overpowered + +// don't blink during ramp, it's irrelevant and annoying on this light +#define BLINK_AT_RAMP_CEIL +#undef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_FLOOR + +// enable extra features +#define USE_SMOOTH_STEPS + +// too big, turn off extra features +#undef USE_TACTICAL_MODE +#undef USE_SOS_MODE + diff --git a/hw/mateminco/mf01s/hwdef.h b/hw/mateminco/mf01s/hwdef.h new file mode 100644 index 0000000..af214b2 --- /dev/null +++ b/hw/mateminco/mf01s/hwdef.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 + +#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/hw/mateminco/mt35-mini/cfg.h b/hw/mateminco/mt35-mini/cfg.h new file mode 100644 index 0000000..1ca5008 --- /dev/null +++ b/hw/mateminco/mt35-mini/cfg.h @@ -0,0 +1,65 @@ +// Mateminco MT35 Mini / Astrolux FT03 +// Copyright (C) 2022-2023 (original author TBD), Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0531" +#include "hwdef-mateminco-mt35-mini.h" +// ATTINY: 85 + +// the button lights up +#define USE_INDICATOR_LED +// the button is visible while main LEDs are on +#define USE_INDICATOR_LED_WHILE_RAMPING + +#define RAMP_SIZE 150 + +// level_calc.py fifth 2 150 7135 1 1 120 FET 1 10 2000 +#define PWM1_LEVELS 1,2,2,3,4,5,6,7,8,9,11,13,14,16,18,21,23,26,28,31,35,38,41,45,49,53,58,63,67,73,78,84,90,96,102,109,116,124,131,139,147,156,165,174,184,194,204,215,226,237,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,3,4,5,6,7,8,9,10,11,12,14,15,16,17,18,20,21,22,24,25,27,28,30,31,33,35,36,38,40,42,43,45,47,49,51,53,55,57,59,62,64,66,68,71,73,75,78,80,83,86,88,91,94,96,99,102,105,108,111,114,117,120,124,127,130,134,137,140,144,148,151,155,159,162,166,170,174,178,182,186,190,195,199,203,208,212,217,221,226,231,235,240,245,250,255 + +#define DEFAULT_LEVEL 46 +#define MAX_1x7135 52 +#define HALFSPEED_LEVEL 20 +#define QUARTERSPEED_LEVEL 10 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 112 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// safe limit ~33% power +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL +#define SIMPLE_UI_STEPS 5 + +// this light should be fine running a bit hotter than most +#undef DEFAULT_THERM_CEIL +#define DEFAULT_THERM_CEIL 55 + +// stop panicking at ~40% power +#define THERM_FASTER_LEVEL 120 +#define MIN_THERM_STEPDOWN 95 // ~600 lumens +#define THERM_RESPONSE_MAGNITUDE 16 // smaller adjustments, big body. default = 64 + +#define DEFAULT_2C_STYLE 1 // enable 2 click turbo (Anduril 1 style) + +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// Allow 3C in Simple UI for switching between smooth and stepped ramping +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +// enable extra features +#define USE_SMOOTH_STEPS + +// too big, turn off extra features +#undef USE_TACTICAL_MODE +#undef USE_SOS_MODE +//#undef USE_RAMP_AFTER_MOON_CONFIG +//#undef USE_RAMP_SPEED_CONFIG +//#undef USE_VOLTAGE_CORRECTION +//#undef USE_2C_STYLE_CONFIG + diff --git a/hw/mateminco/mt35-mini/hwdef.h b/hw/mateminco/mt35-mini/hwdef.h new file mode 100644 index 0000000..015c813 --- /dev/null +++ b/hw/mateminco/mt35-mini/hwdef.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 + +#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/hw/sofirn/blf-lt1-t1616/cfg.h b/hw/sofirn/blf-lt1-t1616/cfg.h new file mode 100644 index 0000000..fc02619 --- /dev/null +++ b/hw/sofirn/blf-lt1-t1616/cfg.h @@ -0,0 +1,110 @@ +// BLF Lantern config options for Anduril using the Attiny1616 +// Copyright (C) 2021-2023 (original author TBD), Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0622" +#include "hwdef-blf-lt1-t1616.h" +// ATTINY: 1616 + +// the button lights up +#define USE_INDICATOR_LED +// the button is visible while main LEDs are on +#define USE_INDICATOR_LED_WHILE_RAMPING +// off mode: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + +// channel modes... +// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO +#define DEFAULT_CHANNEL_MODE CM_AUTO +#define DEFAULT_BLINK_CHANNEL CM_BOTH + +#define CONFIG_WAITING_CHANNEL CM_BOTH +#define CONFIG_BLINK_CHANNEL CM_BOTH + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_BOTH + +// how much to increase total brightness at middle tint +// (0 = 100% brightness, 64 = 200% brightness) +//#define TINT_RAMPING_CORRECTION 26 // prototype, 140% +#define TINT_RAMPING_CORRECTION 10 // production model, 115% + +#define RAMP_SIZE 150 +// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits) +// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) +// level_calc.py 3.333 1 150 7135 32 0.2 600 --pwm 32640 +#define PWM1_LEVELS 4,35,38,41,45,50,55,61,67,74,82,91,100,110,121,133,146,160,175,192,209,227,247,268,291,314,340,366,395,424,456,489,524,560,599,639,681,726,772,820,871,924,979,1036,1096,1158,1222,1289,1359,1431,1506,1584,1664,1747,1834,1923,2015,2111,2209,2311,2416,2524,2636,2751,2870,2992,3118,3247,3380,3518,3659,3803,3952,4105,4262,4423,4589,4759,4933,5111,5294,5482,5674,5871,6073,6279,6491,6707,6928,7155,7386,7623,7865,8113,8365,8624,8888,9157,9432,9713,10000,10292,10591,10895,11206,11523,11846,12175,12511,12853,13202,13557,13919,14287,14663,15045,15434,15830,16233,16644,17061,17486,17919,18358,18805,19260,19723,20193,20671,21156,21650,22152,22662,23180,23706,24241,24784,25335,25895,26464,27041,27627,28222,28826,29439,30060,30691,31332,31981,32640 + +#define DEFAULT_LEVEL 75 +#define MAX_1x7135 75 +#define HALFSPEED_LEVEL 0 // always use tint ramping correction +#define QUARTERSPEED_LEVEL 2 // quarter speed at level 1, full speed at 2+ +//#undef USE_DYNAMIC_UNDERCLOCKING // makes huge bumps in the ramp + +#define USE_SET_LEVEL_GRADUALLY + + +// override default ramp style +#undef RAMP_STYLE +#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped +// set floor and ceiling as far apart as possible +// because this lantern isn't overpowered +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 150 +#define RAMP_DISCRETE_FLOOR 1 +#define RAMP_DISCRETE_CEIL 150 +#define RAMP_DISCRETE_STEPS 7 + +// LT1 can handle heat well, so don't limit simple mode +#define SIMPLE_UI_FLOOR 10 +#define SIMPLE_UI_CEIL 150 +#define SIMPLE_UI_STEPS 5 + +// Allow 3C (or 6C) in Simple UI (toggle smooth or stepped ramping) +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +// allow Aux Config and Strobe Modes in Simple UI +#define USE_EXTENDED_SIMPLE_UI + +#define USE_SOS_MODE +#define USE_SOS_MODE_IN_BLINKY_GROUP + +// the default of 26 looks a bit flat, so increase it +#define CANDLE_AMPLITUDE 40 + +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_COLOR_STROBE_CH1 CM_CH1 +#define POLICE_COLOR_STROBE_CH2 CM_CH2 +// aux red + aux blue are the correct colors, but are dim +//#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +//#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +#undef TACTICAL_LEVELS +#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe + +// party strobe, tac strobe, police, lightning, candle, bike +#define DEFAULT_STROBE_CHANNELS CM_BOTH,CM_BOTH,CM_BOTH,CM_AUTO,CM_AUTO,CM_AUTO + +// the sensor (attiny1616) is nowhere near the emitters +// so thermal regulation can't work +#ifdef USE_THERMAL_REGULATION +#undef USE_THERMAL_REGULATION +#endif + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_FLOOR +#undef BLINK_AT_RAMP_FLOOR +#endif +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif +// except the top... blink at the top +#ifndef BLINK_AT_RAMP_CEIL +#define BLINK_AT_RAMP_CEIL +#endif + +// for consistency with other models +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/sofirn/blf-lt1-t1616/hwdef.c b/hw/sofirn/blf-lt1-t1616/hwdef.c new file mode 100644 index 0000000..9d268a4 --- /dev/null +++ b/hw/sofirn/blf-lt1-t1616/hwdef.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/hw/sofirn/blf-lt1-t1616/hwdef.h b/hw/sofirn/blf-lt1-t1616/hwdef.h new file mode 100644 index 0000000..7c1f10b --- /dev/null +++ b/hw/sofirn/blf-lt1-t1616/hwdef.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 + +#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/hw/sofirn/blf-lt1/cfg.h b/hw/sofirn/blf-lt1/cfg.h new file mode 100644 index 0000000..53c3203 --- /dev/null +++ b/hw/sofirn/blf-lt1/cfg.h @@ -0,0 +1,105 @@ +// BLF Lantern config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0621" +#include "hwdef-blf-lt1.h" +// ATTINY: 85 + +// the button lights up +#define USE_INDICATOR_LED +// the button is visible while main LEDs are on +#define USE_INDICATOR_LED_WHILE_RAMPING +// off mode: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + +// channel modes... +// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO +#define DEFAULT_CHANNEL_MODE CM_AUTO +//#define DEFAULT_BLINK_CHANNEL CM_BOTH // takes too much space + +// how much to increase total brightness at middle tint +// (0 = 100% brightness, 64 = 200% brightness) +//#define TINT_RAMPING_CORRECTION 26 // prototype, 140% +//#define TINT_RAMPING_CORRECTION 10 // production model, 115% +#define TINT_RAMPING_CORRECTION 0 // none + +#define RAMP_SIZE 150 +// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits) +// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) +// level_calc.py 3.333 1 150 7135 32 0.2 600 --pwm 32640 +#define PWM1_LEVELS 32,35,38,41,45,50,55,61,67,74,82,91,100,110,121,133,146,160,175,192,209,227,247,268,291,314,340,366,395,424,456,489,524,560,599,639,681,726,772,820,871,924,979,1036,1096,1158,1222,1289,1359,1431,1506,1584,1664,1747,1834,1923,2015,2111,2209,2311,2416,2524,2636,2751,2870,2992,3118,3247,3380,3518,3659,3803,3952,4105,4262,4423,4589,4759,4933,5111,5294,5482,5674,5871,6073,6279,6491,6707,6928,7155,7386,7623,7865,8113,8365,8624,8888,9157,9432,9713,10000,10292,10591,10895,11206,11523,11846,12175,12511,12853,13202,13557,13919,14287,14663,15045,15434,15830,16233,16644,17061,17486,17919,18358,18805,19260,19723,20193,20671,21156,21650,22152,22662,23180,23706,24241,24784,25335,25895,26464,27041,27627,28222,28826,29439,30060,30691,31332,31981,32640 + +#define DEFAULT_LEVEL 75 +#define MAX_1x7135 75 +#define HALFSPEED_LEVEL 44 +#define QUARTERSPEED_LEVEL 34 +#undef USE_DYNAMIC_UNDERCLOCKING // makes huge bumps in the ramp + +#define USE_SMOOTH_STEPS +//#define USE_SET_LEVEL_GRADUALLY + +// the default of 26 looks a bit flat, so increase it +#define CANDLE_AMPLITUDE 40 + +// override default ramp style +#undef RAMP_STYLE +#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped +// set floor and ceiling as far apart as possible +// because this lantern isn't overpowered +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 150 +#define RAMP_DISCRETE_FLOOR 1 +#define RAMP_DISCRETE_CEIL 150 +#define RAMP_DISCRETE_STEPS 7 + +// LT1 can handle heat well, so don't limit simple mode +#define SIMPLE_UI_FLOOR 10 +#define SIMPLE_UI_CEIL 150 +#define SIMPLE_UI_STEPS 5 + +// Allow 3C in Simple UI for switching between smooth and stepped ramping +#define USE_SIMPLE_UI_RAMPING_TOGGLE +#define USE_EXTENDED_SIMPLE_UI + +// also at Sofirn's request, enable 2 click turbo (Anduril 1 style) +#define DEFAULT_2C_STYLE 1 + + +// the sensor (attiny85) is nowhere near the emitters +// so thermal regulation can't work +#ifdef USE_THERMAL_REGULATION +#undef USE_THERMAL_REGULATION +#endif + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_FLOOR +#undef BLINK_AT_RAMP_FLOOR +#endif +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif +#ifdef BLINK_AT_RAMP_CEIL +#undef BLINK_AT_RAMP_CEIL +#endif + +// too big, turn off extra features +//#undef USE_STEPPED_TINT_RAMPING +#undef USE_MOMENTARY_MODE +#undef USE_TACTICAL_MODE +#undef USE_SOS_MODE +//#undef USE_SIMPLE_UI +//#undef USE_BEACON_MODE +//#undef USE_RAMP_SPEED_CONFIG +#undef USE_RAMP_AFTER_MOON_CONFIG +#undef USE_2C_STYLE_CONFIG +#undef USE_VOLTAGE_CORRECTION +//#undef USE_CHANNEL_PER_STROBE +// party strobe, tac strobe, lightning, candle, bike +#define DEFAULT_STROBE_CHANNELS CM_BOTH,CM_BOTH,CM_AUTO,CM_AUTO,CM_AUTO + +// for consistency with other models +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/sofirn/blf-lt1/hwdef.c b/hw/sofirn/blf-lt1/hwdef.c new file mode 100644 index 0000000..8a4c0e1 --- /dev/null +++ b/hw/sofirn/blf-lt1/hwdef.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/hw/sofirn/blf-lt1/hwdef.h b/hw/sofirn/blf-lt1/hwdef.h new file mode 100644 index 0000000..b113fd4 --- /dev/null +++ b/hw/sofirn/blf-lt1/hwdef.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 + +#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< 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/hw/sofirn/blf-q8-t1616/cfg.h b/hw/sofirn/blf-q8-t1616/cfg.h new file mode 100644 index 0000000..9f0402c --- /dev/null +++ b/hw/sofirn/blf-q8-t1616/cfg.h @@ -0,0 +1,82 @@ +// BLF Q8 config options for Anduril using the Attiny1616 +// Copyright (C) 2021-2023 gchart, Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0613" +#include "hwdef-blf-q8-t1616.h" +#include "wurkkos-cfg.h" // Sofirn lights are closely related to Wurkkos +// ATTINY: 1616 + +// the button lights up +#define USE_INDICATOR_LED +// the button is visible while main LEDs are on +#define USE_INDICATOR_LED_WHILE_RAMPING +// off mode: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + +// copied from Wurkkos TS25 ramp +#define RAMP_SIZE 150 +// 7135 at 75/150 +// level_calc.py 5.7895 2 150 7135 1 0.1 130 FET 1 10 3000 --pwm dyn:74:4096:255:3 +// (with some manual tweaks) +#define PWM1_LEVELS 1,1,2,3,3,4,5,6,7,8,9,11,12,13,15,16,18,19,21,23,26,27,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,71,74,76,78,80,82,85,87,90,93,96,100,103,107,112,116,122,127,133,140,147,154,163,171,182,192,203,215,228,241,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +// non-zero part of FET channel calculated with: +// level_calc.py 3 1 75 7135 1 200 3000 +// (FIXME? there's a visible bump when the FET kicks in, even with just 1/255) +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,7,8,10,11,13,14,16,17,19,21,22,24,26,28,30,32,34,37,39,41,44,46,48,51,54,56,59,62,65,68,71,74,77,81,84,87,91,94,98,102,106,110,114,118,122,126,130,135,139,144,148,153,158,163,168,173,178,184,189,195,200,206,212,218,224,230,236,242,248,255 +#define PWM_TOPS 4095,2701,3200,3586,2518,2778,2834,2795,2705,2587,2455,2582,2412,2247,2256,2091,2062,1907,1860,1802,1737,1605,1542,1477,1412,1347,1284,1222,1162,1105,1050,997,946,898,853,810,768,730,693,658,625,594,564,536,503,485,462,439,418,398,384,366,353,340,327,319,307,298,292,284,280,273,269,266,263,260,258,256,256,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 + +#define MAX_1x7135 75 +#define DEFAULT_LEVEL 50 +#define MIN_THERM_STEPDOWN 60 +#define HALFSPEED_LEVEL 20 +#define QUARTERSPEED_LEVEL 5 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 150 +// 20 38 56 [75] 93 111 130 +#define RAMP_DISCRETE_FLOOR 20 +#define RAMP_DISCRETE_CEIL 130 +#define RAMP_DISCRETE_STEPS 7 + +// at Sofirn's request, use max (150) for the Simple UI ceiling +#define SIMPLE_UI_FLOOR 1 +#define SIMPLE_UI_CEIL 150 +#define SIMPLE_UI_STEPS 5 + +// also at Sofirn's request, enable 2 click turbo (Anduril 1 style) +#define DEFAULT_2C_STYLE 1 + +// enable SOS in the blinkies group +#define USE_SOS_MODE +#define USE_SOS_MODE_IN_BLINKY_GROUP + +// Allow 3C in Simple UI for switching between smooth and stepped ramping +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +// allow Aux Config and Strobe Modes in Simple UI +#define USE_EXTENDED_SIMPLE_UI + +// stop panicking at ~75% power or ~3000 lm, this light has high thermal mass +#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the aux LEDs by default +#define DEFAULT_BLINK_CHANNEL CM_AUX + +// the default of 26 looks a bit rough, so increase it to make it smoother +#define CANDLE_AMPLITUDE 33 + +// don't blink during the ramp; the button LED brightness is sufficient +// to indicate which power channel(s) are being used +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// enable factory reset on 13H without loosening tailcap (required) +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/sofirn/blf-q8-t1616/hwdef.h b/hw/sofirn/blf-q8-t1616/hwdef.h new file mode 100644 index 0000000..d9b981f --- /dev/null +++ b/hw/sofirn/blf-q8-t1616/hwdef.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 + +// 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/hw/sofirn/blf-q8/cfg.h b/hw/sofirn/blf-q8/cfg.h new file mode 100644 index 0000000..29dbcb7 --- /dev/null +++ b/hw/sofirn/blf-q8/cfg.h @@ -0,0 +1,71 @@ +// BLF Q8 config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0611" +#include "hwdef-blf-q8.h" + +#define RAMP_SIZE 150 + +// the button lights up +#define USE_INDICATOR_LED +// the button is visible while main LEDs are on +#define USE_INDICATOR_LED_WHILE_RAMPING +// off mode: high (2) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 2) + +// copied from Emisar D4 ramp +// ../../bin/level_calc.py 1 65 7135 1 0.8 150 +// ... mixed with this: +// ../../bin/level_calc.py 2 150 7135 4 0.33 150 FET 1 10 1500 +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255 + +#define MAX_1x7135 65 +#define DEFAULT_LEVEL 65 +#define HALFSPEED_LEVEL 14 +#define QUARTERSPEED_LEVEL 5 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 120 +// 10 28 46 [65] 83 101 120 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// at Sofirn's request, use max (150) for the Simple UI ceiling +#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR +#define SIMPLE_UI_CEIL 150 +#define SIMPLE_UI_STEPS 5 + +// also at Sofirn's request, enable 2 click turbo (Anduril 1 style) +#define DEFAULT_2C_STYLE 1 + +// enable SOS in the blinkies group +#define USE_SOS_MODE +#define USE_SOS_MODE_IN_BLINKY_GROUP + +// Allow 3C in Simple UI for switching between smooth and stepped ramping +#define USE_SIMPLE_UI_RAMPING_TOGGLE +#define USE_EXTENDED_SIMPLE_UI + +// stop panicking at ~75% power or ~3000 lm, this light has high thermal mass +#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +#define USE_SMOOTH_STEPS + +// too big, remove stuff to make room +#undef USE_SOS_MODE +//#undef USE_RAMP_AFTER_MOON_CONFIG +//#undef USE_RAMP_SPEED_CONFIG +//#undef USE_VOLTAGE_CORRECTION +//#undef USE_2C_STYLE_CONFIG +//#undef USE_TACTICAL_STROBE_MODE +#undef USE_TACTICAL_MODE diff --git a/hw/sofirn/blf-q8/hwdef.h b/hw/sofirn/blf-q8/hwdef.h new file mode 100644 index 0000000..cdf311d --- /dev/null +++ b/hw/sofirn/blf-q8/hwdef.h @@ -0,0 +1,20 @@ +// BLF Q8 driver layout +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// Q8 driver is the same as a D4, basically + +// ... except the Q8 has a lighted button +#ifndef AUXLED_PIN +#define AUXLED_PIN PB4 // pin 3 +#endif + +// ... and slightly different calibration +#ifndef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V +#endif + +// Q8 driver is the same as a D4, basically +#include "hwdef-emisar-d4.h" + diff --git a/hw/sofirn/lt1s-pro/cfg.h b/hw/sofirn/lt1s-pro/cfg.h new file mode 100644 index 0000000..440a03d --- /dev/null +++ b/hw/sofirn/lt1s-pro/cfg.h @@ -0,0 +1,125 @@ +// Sofirn LT1S Pro config file for Anduril +// Copyright (C) 2022-2023 (FIXME) +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0623" +#include "hwdef-sofirn-lt1s-pro.h" +// ATTINY: 1616 + +// off mode: low (1) +// lockout: blinking (3) +// Standby power usage: +// - aux high: 6.9 mA (30 days) +// - aux low: 0.16 mA (3.5 years) +// - red moon: 2.17 mA (96 days) +// - white moon: 1.47 mA (141 days) +// Low mode isn't bright enough to be useful on this light, +// but at least it doesn't drain the battery 3X faster than moon mode. +// (it seriously would be more practical to just use moon instead) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + +// channel modes... +// CM_WHITE, CM_AUTO2, CM_AUTO3, CM_RED, CM_WHITE_RED +#define DEFAULT_CHANNEL_MODE CM_AUTO3 + +#define FACTORY_RESET_WARN_CHANNEL CM_RED +#define FACTORY_RESET_SUCCESS_CHANNEL CM_WHITE + +#define CONFIG_WAITING_CHANNEL CM_RED +#define CONFIG_BLINK_CHANNEL CM_WHITE + +// blink numbers on the main LEDs by default (but allow user to change it) +#define DEFAULT_BLINK_CHANNEL CM_RED + +// how much to increase total brightness at middle tint +// (0 = 100% brightness, 64 = 200% brightness) +// seems unnecessary on this light +#define TINT_RAMPING_CORRECTION 0 + +#define RAMP_SIZE 150 +// use dynamic PWM instead of plain 8-bit +// (so we can get lower lows and a smoother ramp) +// (also, red LEDs use a QX7138 chip which has max PWM speed of 10 kHz, +// and it behaves erratically at full speed, +// so PWM here is 576 clock cycles long to keep the speed low enough) +// +// This first ramp seems a bit too low: 0.2 / 1.9 / 10 / 37 / 109 / 272 / 600 lm +// level_calc.py 5.99 1 150 7135 1 0.2 600 --pwm dyn:77:16383:575:3 +//#define PWM_LEVELS 1,1,2,2,3,4,4,5,6,6,7,8,9,9,10,11,11,12,13,13,14,15,15,16,16,17,18,18,19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,24,24,24,25,26,26,27,28,29,30,32,33,34,36,38,40,42,44,46,48,50,52,55,57,59,62,65,68,70,73,77,80,83,86,90,94,97,101,105,110,114,118,123,128,133,138,143,148,154,160,166,172,178,185,191,198,205,213,220,228,236,244,252,261,270,279,289,298,308,319,329,340,351,363,374,386,399,411,424,438,452,466,480,495,510,526,542,558,575 +//#define PWM_TOPS 16383,10869,13246,8043,11458,12772,10093,11043,11450,9664,9991,10091,10048,8868,8838,8730,7814,7724,7589,6864,6748,6604,6024,5899,5398,5287,5159,4754,4638,4287,3963,3876,3594,3511,3265,3038,2829,2770,2586,2417,2260,2115,1981,1857,1742,1636,1537,1445,1360,1281,1207,1138,1073,1013,957,904,855,848,803,760,720,714,677,643,637,630,599,592,585,577,569,579,570,560,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575 +// +// This ramp is a bit higher: 0.2 / 3 / 19 / 61 / 152 / 320 / 600 lm +// level_calc.py 4.001 1 150 7135 1 0.2 600 --pwm dyn:78:16383:575:3.333 +#define PWM1_LEVELS 1,1,2,4,5,6,7,8,9,10,12,13,14,15,17,18,19,21,22,23,24,25,26,27,28,29,30,31,32,33,33,34,34,35,35,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,37,37,38,38,39,40,40,41,43,44,45,47,48,50,52,54,56,59,62,64,67,69,72,75,78,81,84,87,90,94,97,101,104,108,112,116,120,124,128,133,137,142,147,151,156,161,167,172,177,183,189,194,200,206,213,219,226,232,239,246,253,260,268,275,283,291,299,307,316,324,333,342,351,361,370,380,390,400,410,420,431,442,453,464,476,487,499,511,523,536,549,562,575 +#define PWM_TOPS 16383,8174,7823,14429,13603,12806,12046,11328,10652,10017,10402,9742,9134,8575,8615,8089,7605,7536,7093,6684,6307,5959,5636,5337,5060,4802,4562,4337,4127,3929,3633,3468,3216,3077,2862,2744,2559,2390,2234,2091,1960,1838,1727,1623,1528,1439,1357,1280,1209,1143,1081,1024,970,919,872,828,787,770,732,716,682,668,654,624,611,613,600,587,587,574,573,571,569,566,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575 +// TODO? 200% power at top of ramp on white blend mode +// 2nd table handles "200% power" turbo +//#define PWM2_LEVELS ... +// tops for PWM2 +//#define PWM3_LEVELS ... +#define MAX_1x7135 75 +#define MIN_THERM_STEPDOWN 75 // should be above highest dyn_pwm level +#define HALFSPEED_LEVEL 12 +#define QUARTERSPEED_LEVEL 5 + +// the default of 26 looks a bit flat, so increase it +#define CANDLE_AMPLITUDE 40 + +// override default ramp style +#undef RAMP_STYLE +#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped +// set floor and ceiling as far apart as possible +// because this lantern isn't overpowered +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 150 +//#define RAMP_DISCRETE_FLOOR 17 // 17 50 83 116 150 +#define RAMP_DISCRETE_FLOOR 1 // 1 25 50 75 100 125 150 +#define RAMP_DISCRETE_CEIL 150 +#define RAMP_DISCRETE_STEPS 7 + +// LT1S can handle heat well, so don't limit simple mode +//#define SIMPLE_UI_FLOOR 10 // 10 45 80 115 150 +#define SIMPLE_UI_FLOOR 1 +#define SIMPLE_UI_CEIL 150 +#define SIMPLE_UI_STEPS 7 + +// Allow 3C (or 6C) in Simple UI (toggle smooth or stepped ramping) +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +// allow Aux Config and Strobe Modes in Simple UI +#define USE_EXTENDED_SIMPLE_UI + +// turn on at med-low brightness by default (level 50/150, or ramp step 3/7) +// (also sets lockout mode 2H to a useful level) +#define DEFAULT_MANUAL_MEMORY 50 +// reset to default after being off for 10 minutes +#define DEFAULT_MANUAL_MEMORY_TIMER 10 + +// enable 2 click turbo (Anduril 1 style) +#define DEFAULT_2C_STYLE 1 + +#define USE_SOS_MODE +#define USE_SOS_MODE_IN_BLINKY_GROUP + +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_COLOR_STROBE_CH1 CM_RED +#define POLICE_COLOR_STROBE_CH2 CM_WHITE + +#undef TACTICAL_LEVELS +#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe + +// don't blink while ramping +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif +#ifdef BLINK_AT_RAMP_FLOOR +#undef BLINK_AT_RAMP_FLOOR +#endif +#ifdef BLINK_AT_RAMP_CEIL +#undef BLINK_AT_RAMP_CEIL +#endif +// without this, it's really hard to tell when ramping up stops +#define BLINK_AT_RAMP_CEIL + +#define USE_SOFT_FACTORY_RESET diff --git a/hw/sofirn/lt1s-pro/hwdef.c b/hw/sofirn/lt1s-pro/hwdef.c new file mode 100644 index 0000000..90c2c07 --- /dev/null +++ b/hw/sofirn/lt1s-pro/hwdef.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/hw/sofirn/lt1s-pro/hwdef.h b/hw/sofirn/lt1s-pro/hwdef.h new file mode 100644 index 0000000..ae6b3bf --- /dev/null +++ b/hw/sofirn/lt1s-pro/hwdef.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 + +#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/hw/sofirn/sc21-pro/cfg.h b/hw/sofirn/sc21-pro/cfg.h new file mode 100644 index 0000000..8fd2dee --- /dev/null +++ b/hw/sofirn/sc21-pro/cfg.h @@ -0,0 +1,14 @@ +// Sofirn SC21 Pro - same setup as a Wurkkos TS10, but with the aux indicator on while ramping +// Copyright (C) 2022-2023 (FIXME) +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "cfg-wurkkos-ts10.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0632" +// ATTINY: 1616 + +// turn on the aux LED while main LED is on +#ifndef USE_INDICATOR_LED_WHILE_RAMPING +#define USE_INDICATOR_LED_WHILE_RAMPING +#endif diff --git a/hw/sofirn/sp10-pro/cfg.h b/hw/sofirn/sp10-pro/cfg.h new file mode 100644 index 0000000..0e2f28d --- /dev/null +++ b/hw/sofirn/sp10-pro/cfg.h @@ -0,0 +1,81 @@ +// Sofirn SP10 Pro config options for Anduril +// Copyright (C) 2022-2023 (original author TBD), Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0631" +#include "hwdef-sofirn-sp10-pro.h" +// ATTINY: 1616 + +// 1....15: level_calc.py 3.01 1 15 7135 1 0.1 2 --pwm dyn:15:64:64 +// 16..150: level_calc.py 5.01 1 135 7135 1 2 800 --pwm dyn:49:3072:255:3.0 +#define RAMP_SIZE 150 +#define _PWM1_LEVELS_ 1, 2, 4, 6, 9,12,15,19,23,28,34,41,48,55,64 +#define _PWM1_TOPS_ 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64 +#define _PWM2_LEVELS_ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 13, 13, 14, 15, 15, 16, 17, 18, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 33, 34, 35, 37, 38, 39, 41, 42, 44, 46, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 70, 72, 74, 77, 79, 82, 85, 88, 90, 93, 96, 99,103,106,109,113,116,120,123,127,131,135,139,143,147,151,156,160,165,170,175,180,185,190,195,201,206,212,218,223,230,236,242,248,255 +#define _PWM2_TOPS_ 3072,1960,2372,1476,2097,1572,1920,1570,1777,1524,1646,1454,1286,1369,1234,1115,1011,918,837,894,823,759,702,650,603,560,522,487,455,425,398,374,351,330,310,292,275,259,280,265,251,266,253,240,252,240,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#define PWM1_LEVELS _PWM1_LEVELS_,_PWM2_TOPS_ +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,_PWM2_LEVELS_ +#define PWM_TOPS _PWM1_TOPS_,_PWM2_TOPS_ + +#define MAX_1x7135 15 +#define HALFSPEED_LEVEL 15 +#define QUARTERSPEED_LEVEL 15 +#define DEFAULT_LEVEL 50 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 150 +// 1 25 50 [75] 100 125 150 +#define RAMP_DISCRETE_FLOOR 1 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// at Sofirn's request, use max (150) for the Simple UI ceiling +// 15 48 [82] 116 150 +#define SIMPLE_UI_FLOOR MAX_1x7135 +#define SIMPLE_UI_CEIL 150 +#define SIMPLE_UI_STEPS 5 + +// turn on at ~6 lm by default (level 50/150, or ramp step 2/5 or 3/7) +// (also sets lockout mode 2H to a useful level) +#define DEFAULT_MANUAL_MEMORY 50 +// reset to default after being off for 10 minutes +#define DEFAULT_MANUAL_MEMORY_TIMER 10 + +// enable SOS in the blinkies group +#define USE_SOS_MODE +#define USE_SOS_MODE_IN_BLINKY_GROUP + +// Allow 3C in Simple UI for switching between smooth and stepped ramping +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +// and finally, set the default ramp style to Stepped +#undef RAMP_STYLE +#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped + +// stop panicking at ~30% power +#define THERM_FASTER_LEVEL 105 +#define MIN_THERM_STEPDOWN 65 // must be > end of dynamic PWM range + +// slow down party strobe; this driver can't pulse for too short a time +//#define PARTY_STROBE_ONTIME 8 +#define STROBE_OFF_LEVEL 1 // keep the regulator chip on between pulses + +// the default of 26 looks a bit flat, so increase it +#define CANDLE_AMPLITUDE 50 + +// enable 2 click turbo (replaces USE_2C_MAX_TURBO) +#define DEFAULT_2C_STYLE 1 + +// don't blink during the ramp or at the ceiling +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif +#ifdef BLINK_AT_RAMP_CEIL +#undef BLINK_AT_RAMP_CEIL +#endif + + +// enable factory reset on 13H without loosening tailcap +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/sofirn/sp10-pro/hwdef.c b/hw/sofirn/sp10-pro/hwdef.c new file mode 100644 index 0000000..42844a7 --- /dev/null +++ b/hw/sofirn/sp10-pro/hwdef.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/hw/sofirn/sp10-pro/hwdef.h b/hw/sofirn/sp10-pro/hwdef.h new file mode 100644 index 0000000..5cef6a7 --- /dev/null +++ b/hw/sofirn/sp10-pro/hwdef.h @@ -0,0 +1,157 @@ +// 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 + */ + +#define ATTINY 1616 +#include + +#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 + +// 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 // 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 +#define CH1_PIN PB5 +#define CH1_PWM TCA0.SINGLE.CMP2BUF // PB5 is Alternate MUX for TCA Compare 2 + +// Big channel +#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 + +// Raw ADC readings at 4.4V and 2.2V +// calibrate the voltage readout here +// estimated / calculated values are: +// (voltage - D1) * (R2/(R2+R1) * 1024 / 1.1) +// Resistors are 300,000 and 100,000 +#ifndef ADC_44 +#define ADC_44 1023 // raw value at 4.40V +#endif +#ifndef ADC_22 +#define ADC_22 512 // raw value at 2.20V +#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 = PIN1_bm; // Boost enable pin + VPORTB.DIR = PIN0_bm // big PWM channel + | PIN5_bm; // small PWM channel + //VPORTC.DIR = ...; + + // enable pullups on the input pins to reduce power + PORTA.PIN0CTRL = PORT_PULLUPEN_bm; + //PORTA.PIN1CTRL = PORT_PULLUPEN_bm; // Boost enable pin + 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; + PORTA.PIN7CTRL = PORT_PULLUPEN_bm; + + //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; // e-switch + //PORTB.PIN4CTRL = PORT_PULLUPEN_bm; // Voltage divider + //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // Small PWM channel + + 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 + 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; + PWM_TOP = PWM_TOP_INIT; + TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc + | TCA_SINGLE_ENABLE_bm; +} + + +// set fuses, these carry over to the ELF file +// we need this for enabling BOD in Active Mode from the factory. +// settings can be verified / dumped from the ELF file using this +// command: avr-objdump -d -S -j .fuse anduril.elf +FUSES = { + .WDTCFG = FUSE_WDTCFG_DEFAULT, // Watchdog Configuration + .BODCFG = FUSE_ACTIVE0, // BOD Configuration + .OSCCFG = FUSE_OSCCFG_DEFAULT, // Oscillator Configuration + .TCD0CFG = FUSE_TCD0CFG_DEFAULT, // TCD0 Configuration + .SYSCFG0 = FUSE_SYSCFG0_DEFAULT, // System Configuration 0 + .SYSCFG1 = FUSE_SYSCFG1_DEFAULT, // System Configuration 1 + .APPEND = FUSE_APPEND_DEFAULT, // Application Code Section End + .BOOTEND = FUSE_BOOTEND_DEFAULT, // Boot Section End +}; + + +#define LAYOUT_DEFINED + diff --git a/hw/sofirn/sp36-t1616/cfg.h b/hw/sofirn/sp36-t1616/cfg.h new file mode 100644 index 0000000..cb29e4f --- /dev/null +++ b/hw/sofirn/sp36-t1616/cfg.h @@ -0,0 +1,37 @@ +// Sofirn SP36 (small Q8) config options for Anduril using the Attiny1616 +// Copyright (C) 2021-2023 (FIXME) +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as the BLF Q8, mostly +#include "cfg-blf-q8-t1616.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0614" +// ATTINY: 1616 + +// voltage readings were a little high with the Q8 value +#undef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V, not 0.35V + +// the high button LED mode on this light uses too much power +// off mode: low (1) +// lockout: blinking (3) +#ifdef INDICATOR_LED_DEFAULT_MODE +#undef INDICATOR_LED_DEFAULT_MODE +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) +#endif + +// don't blink during the ramp; the button LED brightness is sufficient +// to indicate which power channel(s) are being used +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif +#ifdef BLINK_AT_RAMP_CEIL +#undef BLINK_AT_RAMP_CEIL +#endif + +// stop panicking at ~60% power or ~3000 lm +#ifdef THERM_FASTER_LEVEL +#undef THERM_FASTER_LEVEL +#endif +#define THERM_FASTER_LEVEL 130 diff --git a/hw/sofirn/sp36/cfg.h b/hw/sofirn/sp36/cfg.h new file mode 100644 index 0000000..3661686 --- /dev/null +++ b/hw/sofirn/sp36/cfg.h @@ -0,0 +1,36 @@ +// Sofirn SP36 (small Q8) config options for Anduril +// Copyright (C) 2018-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as the BLF Q8, mostly +#include "cfg-blf-q8.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0612" + +// voltage readings were a little high with the Q8 value +#undef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V, not 0.35V + +// the high button LED mode on this light uses too much power +// off mode: low (1) +// lockout: blinking (3) +#ifdef INDICATOR_LED_DEFAULT_MODE +#undef INDICATOR_LED_DEFAULT_MODE +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) +#endif + +// don't blink during the ramp; the button LED brightness is sufficient +// to indicate which power channel(s) are being used +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif +#ifdef BLINK_AT_RAMP_CEIL +#undef BLINK_AT_RAMP_CEIL +#endif + +// stop panicking at ~60% power or ~3000 lm +#ifdef THERM_FASTER_LEVEL +#undef THERM_FASTER_LEVEL +#endif +#define THERM_FASTER_LEVEL 130 diff --git a/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/cfg.h b/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/cfg.h new file mode 100644 index 0000000..460346e --- /dev/null +++ b/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/cfg.h @@ -0,0 +1,116 @@ +// thefreeman's BCK-FWAA-MP3432 (li-ion / AA) +// Copyright (C) 2023 TBD (thefreeman), Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "1632" +#include "hwdef-thefreeman-boost-fwaa-mp3432-hdr-dac-rgb.h" +// ATTINY: 1616 + +// HPRsense : 4.2+0.3+20 = 24.5mR (DMN1004UFDF+trace resistance+20mR) +// R1=165k Vsense=49.02 Iout=2001mA +// LPRsense : 2R +// transition DAC lvl 14, ramp lvl 51 +// fifth power ramp 0.06mA to 2001mA + +#define RAMP_SIZE 150 + +// 4 ramp segments: +// - low 0.55V +// - low 2.5V +// - high 0.55V +// - high 2.5V +// PWM1: DAC Data +#define PWM1_LEVELS 2, 3, 4, 5, 6, 8, 9, 11, 14, 16, 19, 23, 26, 31, 35, 41, 47, 54, 61, 69, 78, 89,100,112,125,140,155,173,191,212,234, \ + 56, 62, 68, 74, 82, 89, 97,106,115,125,136,147,159,172,186,200,215,232,249, \ + 14, 15, 17, 18, 19, 20, 22, 23, 25, 26, 28, 30, 32, 34, 36, 38, 40, 43, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81, 86, 90, 95, 99,104,109,114,120,126,131,138,144,150,157,164,171,179,187,195,203,212,221,230,239,249, \ + 57, 59, 61, 64, 66, 69, 72, 74, 77, 80, 83, 86, 90, 93, 96,100,103,107,111,115,119,123,127,132,136,141,145,150,155,160,166,171,176,182,188,194,200,206,213,219,226,233,240,247,255 +// PWM Tops: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20) +#define PWM_TOPS 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \ + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 + +#define MAX_1x7135 50 +#define DEFAULT_LEVEL 44 +#define HDR_ENABLE_LEVEL_MIN 51 // when HDR FET turns ON + +// no PWM, so MCU clock speed can be slow +#define HALFSPEED_LEVEL 46 +#define QUARTERSPEED_LEVEL 45 // seems to run fine at 10kHz/4, try reducing more + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 // ~50% power, ~??? mA / ??? lm +#define RAMP_DISCRETE_FLOOR 1 +#define RAMP_DISCRETE_CEIL 130 +#define RAMP_DISCRETE_STEPS 7 + +// 20 [45] 70 95 120 +#define SIMPLE_UI_FLOOR 20 +#define SIMPLE_UI_CEIL 120 // ~37% power, ~??? mA / ??? lm +#define SIMPLE_UI_STEPS 5 + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// thermal config + +// temperature limit +#define THERM_FASTER_LEVEL 130 +#define MIN_THERM_STEPDOWN MAX_1x7135 + +//#define THERM_LOOKAHEAD 2 // 4 by default -> decrease for longer turbo +//#define THERM_NEXT_WARNING_THRESHOLD 48 // 24 by default -> increase for fewer adjustments (more stable output on large time scale) +//#define THERM_RESPONSE_MAGNITUDE 16 // 64 by default -> decrease for smaller adjustments (removes dip post turbo) +//#define THERM_WINDOW_SIZE 1 // 2 by default -> decrease for tighter temperature regulation + + +// UI + +//#define SIMPLE_UI_ACTIVE 0 // advanced UI by default + +// allow Aux Config and Strobe Modes in Simple UI +#define USE_EXTENDED_SIMPLE_UI + +// Allow 3C in Simple UI for switching between smooth and stepped ramping +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +#define DEFAULT_2C_STYLE 1 // enable 2 click turbo + + +// AUX + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default +#define DEFAULT_BLINK_CHANNEL CM_MAIN + +// use aux red + aux blue for police strobe +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_STROBE_USES_AUX +#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// the aux LEDs are front-facing, so turn them off while main LEDs are on +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + + +// Misc + +#define PARTY_STROBE_ONTIME 1 // slow down party strobe +#define STROBE_OFF_LEVEL 1 // keep the regulator chip on between pulses + +// smoother candle mode with bigger oscillations +#define CANDLE_AMPLITUDE 40 + +// enable 13H factory reset so it can be used on tail e-switch lights +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/hwdef.h b/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/hwdef.h new file mode 100644 index 0000000..9126a1a --- /dev/null +++ b/hw/thefreeman/boost-fwaa-mp3432-hdr-dac-rgb/hwdef.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 + +#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/hw/thefreeman/boost21-mp3431-hdr-dac-argb/cfg.h b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/cfg.h new file mode 100644 index 0000000..66cc2d0 --- /dev/null +++ b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/cfg.h @@ -0,0 +1,119 @@ +// thefreeman's BST21 BST20-FWxA (no button LED) +// Copyright (C) 2023 TBD (thefreeman), Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "1631" +#include "hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.h" +// ATTINY: 1616 + +// HPRsense : 1.7+0.3+5 = 7mR (DMN22M5UFG+trace resistance+5mR) +// Vsense=42.46mV, R1= 191k +// LPRsense : 1R +// transition DAC level 8, ramp level 45 +// fifth power ramp 0.1mA to 6066mA + +#define RAMP_SIZE 150 + +// 4 ramp segments: +// - low 0.55V +// - low 2.5V +// - high 0.55V +// - high 2.5V +// PWM1: DAC Data +#define PWM1_LEVELS 2, 3, 4, 5, 7, 9, 11, 13, 16, 19, 23, 28, 33, 39, 45, 53, 61, 71, 81, 93,106,121,137,155,175,196,220,246, \ + 60, 67, 74, 82, 91,100,110,121,133,146,159,174,190,207,224,244, \ + 8, 9, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 23, 24, 26, 27, 29, 31, 33, 35, 37, 40, 42, 45, 47, 50, 53, 56, 59, 62, 66, 69, 73, 77, 81, 85, 90, 94, 99,104,109,114,120,126,132,138,144,151,158,165,173,180,188,196,205,214,223,232,242,252, \ + 57, 60, 62, 65, 67, 70, 73, 76, 78, 82, 85, 88, 91, 95, 98,102,105,109,113,117,121,126,130,135,139,144,149,154,159,164,170,175,181,187,193,199,206,212,219,225,232,240,247,255 +// PWM Tops: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20) +#define PWM_TOPS 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \ + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 + +#define MAX_1x7135 44 +#define DEFAULT_LEVEL 44 +#define HDR_ENABLE_LEVEL_MIN 45 // when HDR FET turns ON + +// no PWM, so MCU clock speed can be slow +#define HALFSPEED_LEVEL 41 +#define QUARTERSPEED_LEVEL 40 // seems to run fine at 10kHz/4, try reducing more + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 // 50% / 3A / 1000 lm +// 1 22 [44] 65 87 108 130 +#define RAMP_DISCRETE_FLOOR 1 +#define RAMP_DISCRETE_CEIL 130 +#define RAMP_DISCRETE_STEPS 7 + +// 20 [45] 70 95 120 +#define SIMPLE_UI_FLOOR 20 +#define SIMPLE_UI_CEIL 120 // ~2.25A / ~750 lm +#define SIMPLE_UI_STEPS 5 + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// thermal config + +// temperature limit +#define THERM_FASTER_LEVEL 130 // stop panicking at 50%/3A +#define MIN_THERM_STEPDOWN MAX_1x7135 + +//#define THERM_LOOKAHEAD 4 // 4 by default -> decrease for longer turbo +#define THERM_NEXT_WARNING_THRESHOLD 48 // 24 by default -> increase for fewer adjustments (more stable output on large time scale) +#define THERM_RESPONSE_MAGNITUDE 32 // 64 by default -> decrease for smaller adjustments (removes dip post turbo) +//#define THERM_WINDOW_SIZE 1 // 2 by default -> decrease for tighter temperature regulation + + +// UI + +//#define SIMPLE_UI_ACTIVE 0 // advanced UI by default + +// allow Aux Config and Strobe Modes in Simple UI +#define USE_EXTENDED_SIMPLE_UI + +// Allow 3C in Simple UI for switching between smooth and stepped ramping +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +#define DEFAULT_2C_STYLE 1 // enable 2 click turbo + + +// AUX + +//#define USE_BUTTON_LED + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default +#define DEFAULT_BLINK_CHANNEL CM_MAIN + +// use aux red + aux blue for police strobe +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_STROBE_USES_AUX +#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// the aux LEDs are front-facing, so turn them off while main LEDs are on +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + + +// Misc + +#define PARTY_STROBE_ONTIME 1 // slow down party strobe +#define STROBE_OFF_LEVEL 1 // keep the regulator chip on between pulses + +// smoother candle mode with bigger oscillations +#define CANDLE_AMPLITUDE 40 + +// enable 13H factory reset so it can be used on tail e-switch lights +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.c b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.c new file mode 100644 index 0000000..31feec9 --- /dev/null +++ b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.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/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.h b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.h new file mode 100644 index 0000000..1c35014 --- /dev/null +++ b/hw/thefreeman/boost21-mp3431-hdr-dac-argb/hwdef.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 + +#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/hw/thefreeman/lin16dac/cfg.h b/hw/thefreeman/lin16dac/cfg.h new file mode 100644 index 0000000..20d2bf5 --- /dev/null +++ b/hw/thefreeman/lin16dac/cfg.h @@ -0,0 +1,61 @@ +// thefreeman's Linear 16 driver using DAC control +// Copyright (C) 2021-2023 (FIXME) +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "1630" +#include "hwdef-thefreeman-lin16dac.h" +// ATTINY: 1616 + +// the button lights up +#define USE_INDICATOR_LED +// the button is visible while main LEDs are on +#define USE_INDICATOR_LED_WHILE_RAMPING +// off mode: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + +// level_calc.py ninth 2 150 7135 1 0.03 6.4 7135 1 6.3 1600 +#define RAMP_SIZE 150 + +// 4 ramp segments: +// - low 0.55V +// - low 2.5V +// - high 0.55V +// - high 2.5V +// PWM1: DAC Data +// FIXME: ramp stalls with 8 duplicate levels in a row +// (maybe use 1.1V Vref during that part of the ramp?) +#define PWM1_LEVELS 25, 25, 33, 41, 41, 50, 58, 66, 75, 83, 92,108,117,133,150,167,192,209,234, \ + 58, 64, 71, 80, 90, 99,110,121,134,149,163,180,198,218,241, \ + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 10, 11, 11, 12, 13, 14, 15, 16, 18, 19, 20, 22, 23, 25, 26, 28, 30, 32, 34, 36, 39, 41, 44, 47, 50, 53, 56, 59, 63, 67, 71, 75, 79, 84, 89, 94,100,105,112,118,124,131,139,146,154,163,172,181,191,201,212,223,234,246, \ + 57, 60, 63, 66, 69, 73, 76, 80, 84, 88, 93, 97,102,107,112,117,123,129,135,141,147,154,161,169,176,184,193,201,210,220,229,239,250,255 +// PWM Tops: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20) +#define PWM_TOPS 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \ + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ + 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 + +#define MAX_1x7135 34 +#define HDR_ENABLE_LEVEL_MIN 35 // bottom level of top half of the ramp +#define HALFSPEED_LEVEL 255 // always run at 1/4th speed +#define QUARTERSPEED_LEVEL 255 // because DAC doesn't use PWM + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 120 +// 10, 28, 46, [65], 83, 101, 120 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 7 + +// stop panicking at ~30% power +#define THERM_FASTER_LEVEL 123 + +// enable 2 click turbo +#define DEFAULT_2C_STYLE 1 + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + diff --git a/hw/thefreeman/lin16dac/hwdef.c b/hw/thefreeman/lin16dac/hwdef.c new file mode 100644 index 0000000..07c7f9e --- /dev/null +++ b/hw/thefreeman/lin16dac/hwdef.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/hw/thefreeman/lin16dac/hwdef.h b/hw/thefreeman/lin16dac/hwdef.h new file mode 100644 index 0000000..c0d0638 --- /dev/null +++ b/hw/thefreeman/lin16dac/hwdef.h @@ -0,0 +1,137 @@ +// 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 + */ + +#define ATTINY 1616 +#include + +#define HWDEF_C_FILE hwdef-thefreeman-lin16dac.c + +// allow using aux LEDs as extra channel modes +#include "chan-aux.h" + +// channel modes: +// * 0. main LEDs +// * 1+. aux RGB +#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 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 + +// Opamp enable +// For turning on and off the op-amp +#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 +#define VOLTAGE_FUDGE_FACTOR 0 // using a PFET so no appreciable drop +#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 + // (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 + 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 ouput + //PORTA.PIN7CTRL = PORT_PULLUPEN_bm; // Op-amp enable pin + + PORTB.PIN0CTRL = PORT_PULLUPEN_bm; + PORTB.PIN1CTRL = 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; // e-switch + //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 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/hw/wurkkos/fc13/cfg.h b/hw/wurkkos/fc13/cfg.h new file mode 100644 index 0000000..56161b4 --- /dev/null +++ b/hw/wurkkos/fc13/cfg.h @@ -0,0 +1,17 @@ +// Wurkkos FC13, like a TS25 but with a RGB button and no front aux +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// ATTINY: 1616 +#include "cfg-wurkkos-ts25.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0716" + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// turn on the aux LEDs while main LEDs are on +#define USE_AUX_RGB_LEDS_WHILE_ON 20 +#define USE_INDICATOR_LED_WHILE_RAMPING + diff --git a/hw/wurkkos/ts10-rgbaux/cfg.h b/hw/wurkkos/ts10-rgbaux/cfg.h new file mode 100644 index 0000000..6770c47 --- /dev/null +++ b/hw/wurkkos/ts10-rgbaux/cfg.h @@ -0,0 +1,85 @@ +// Wurkkos TS10 (RGB aux version) config options for Anduril +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// most things are the same as TS25 +#include "cfg-wurkkos-ts25.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0713" +// ATTINY: 1616 + +///// apply some config from the TS10 ///// + +// use the TS10 ramp, not the TS25 ramp +#undef PWM1_LEVELS +#undef PWM2_LEVELS +#undef PWM_TOPS +#undef MAX_1x7135 +#undef MIN_THERM_STEPDOWN +#undef HALFSPEED_LEVEL +#undef QUARTERSPEED_LEVEL +#undef DEFAULT_LEVEL +#undef RAMP_SMOOTH_FLOOR +#undef RAMP_SMOOTH_CEIL +#undef RAMP_DISCRETE_FLOOR +#undef RAMP_DISCRETE_CEIL +#undef RAMP_DISCRETE_STEPS +#undef SIMPLE_UI_FLOOR +#undef SIMPLE_UI_CEIL +#undef SIMPLE_UI_STEPS +#undef THERM_FASTER_LEVEL + +// 7135 at 90/150 +// level_calc.py 5.7895 2 150 7135 0 0.1 125.25 FET 1 10 1200 --pwm dyn:61:4096:255:2.5 --clock 5:11:2.0 +// (with heavy manual tweaks up to ~15/150) +#define PWM1_LEVELS 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 23, 24, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 35, 36, 37, 38, 40, 41, 43, 45, 47, 50, 53, 56, 60, 63, 67, 71, 75, 79, 84, 89, 94, 99,104,110,116,122,129,136,143,150,158,166,174,183,192,202,211,222,232,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 5, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 29, 31, 34, 37, 39, 42, 45, 48, 51, 54, 58, 61, 65, 68, 72, 76, 80, 84, 88, 93, 97,102,107,112,117,122,127,133,139,145,151,157,163,170,177,183,191,198,205,213,221,229,238,246,255 +#define PWM_TOPS 4095,2893,3917,2806,3252,2703,2684,2660,2640,2370,3000,2900,2630,2549,2246,2193,2030,1961,1889,1716,1642,1569,1497,1428,1290,1232,1176,1122,1070,976,932,890,849,779,745,685,656,605,579,536,514,476,457,424,407,379,364,340,327,314,302,291,280,276,266,262,257,253,253,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#define MAX_1x7135 90 +#define MIN_THERM_STEPDOWN 60 +#define HALFSPEED_LEVEL 11 +#define QUARTERSPEED_LEVEL 5 +#define DEFAULT_LEVEL 50 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 +// 20 38 56 [75] 93 111 130 +// 10 30 50 70 [90] 110 130 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL 130 +#define RAMP_DISCRETE_STEPS 7 + +// at Wurkkos's request, reduce the Simple UI ceiling a little bit +// (i.e. not 150; original config had it at 144/150, or DD FET 204/255) +// 20 47 [75] 102 130 +// 10 30 50 70 [90] 110 130 +#define SIMPLE_UI_FLOOR 10 +#define SIMPLE_UI_CEIL 130 +#define SIMPLE_UI_STEPS 7 + +// stop panicking at ~50% power +#define THERM_FASTER_LEVEL 130 // throttle back faster when high + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the aux LEDs by default +#undef DEFAULT_BLINK_CHANNEL +#define DEFAULT_BLINK_CHANNEL CM_AUXWHT + +// the aux LEDs are pretty bright; set the high-mode threshold a bit higher +// (default is 15) +#define POST_OFF_VOLTAGE_BRIGHTNESS 25 + +// the default of 26 looks a bit rough, so increase it to make it smoother +#define CANDLE_AMPLITUDE 33 + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// enable factory reset on 13H without loosening tailcap (required) +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/wurkkos/ts10/cfg.h b/hw/wurkkos/ts10/cfg.h new file mode 100644 index 0000000..90839e7 --- /dev/null +++ b/hw/wurkkos/ts10/cfg.h @@ -0,0 +1,85 @@ +// Wurkkos TS10 (originally used Sofirn SP36-t1616 firmware) config options for Anduril using the Attiny1616 +// Copyright (C) 2022-2023 (FIXME) +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// same as the BLF Q8 T1616, mostly (added Dynamic PWM) +#define MODEL_NUMBER "0714" +#include "hwdef-wurkkos-ts10.h" +#include "wurkkos-cfg.h" +// ATTINY: 1616 + +// uses forward-facing aux LEDs +#define USE_INDICATOR_LED +// don't turn on the aux LEDs while main LEDs are on +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif +// the high button LED mode on this light uses too much power, default to low +// off mode: low (1) +// lockout: blinking (3) +#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + +// voltage readings were a little high with the Q8 value +#undef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V, not 0.35V + +#define RAMP_SIZE 150 + +#if 0 // 2022 version by SammysHP, smooth but only level 1/150 is underclocked +// level 1 by hand, for the rest +// level_calc.py 7.01 2 149 7135 3 0.5 125 FET 1 10 1200 --pwm dyn:63:2048:255 +#define PWM1_LEVELS 1,3,3,4,5,6,7,8,9,10,12,13,14,16,17,19,20,22,24,25,27,29,31,33,35,37,40,42,44,47,49,52,54,57,59,62,64,67,70,72,75,77,80,82,85,87,89,91,93,95,96,98,99,100,100,101,100,100,99,97,95,93,90,86,82,87,91,96,100,106,111,116,122,128,134,141,147,155,162,169,177,186,194,203,213,222,232,243,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,6,7,9,11,12,14,16,18,20,22,24,27,29,31,34,37,39,42,45,48,51,54,57,61,64,68,72,75,79,83,88,92,97,101,106,111,116,121,126,132,138,144,150,156,162,169,176,183,190,197,205,213,221,229,237,246,255 +#define PWM_TOPS 2047,2047,1198,1322,1584,1676,1701,1691,1662,1622,1774,1703,1631,1692,1613,1639,1558,1564,1559,1478,1464,1444,1420,1392,1361,1329,1331,1293,1256,1246,1207,1192,1152,1133,1094,1074,1035,1013,991,954,932,897,875,842,820,790,760,731,704,678,646,622,593,566,534,510,478,452,423,393,364,338,310,280,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#define MAX_1x7135 90 +#define HALFSPEED_LEVEL 2 +#define QUARTERSPEED_LEVEL 2 +#endif + +// 7135 at 90/150 +// level_calc.py 5.7895 2 150 7135 0 0.1 125.25 FET 1 10 1200 --pwm dyn:61:4096:255:2.5 --clock 5:11:2.0 +// (with heavy manual tweaks up to ~15/150) +#define PWM1_LEVELS 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 23, 24, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 35, 36, 37, 38, 40, 41, 43, 45, 47, 50, 53, 56, 60, 63, 67, 71, 75, 79, 84, 89, 94, 99,104,110,116,122,129,136,143,150,158,166,174,183,192,202,211,222,232,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 5, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 29, 31, 34, 37, 39, 42, 45, 48, 51, 54, 58, 61, 65, 68, 72, 76, 80, 84, 88, 93, 97,102,107,112,117,122,127,133,139,145,151,157,163,170,177,183,191,198,205,213,221,229,238,246,255 +#define PWM_TOPS 4095,2893,3917,2806,3252,2703,2684,2660,2640,2370,3000,2900,2630,2549,2246,2193,2030,1961,1889,1716,1642,1569,1497,1428,1290,1232,1176,1122,1070,976,932,890,849,779,745,685,656,605,579,536,514,476,457,424,407,379,364,340,327,314,302,291,280,276,266,262,257,253,253,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#define MAX_1x7135 90 +#define MIN_THERM_STEPDOWN 60 +#define HALFSPEED_LEVEL 11 +#define QUARTERSPEED_LEVEL 5 +#define DEFAULT_LEVEL 50 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 +// 10 30 50 70 [90] 110 130 +#define RAMP_DISCRETE_FLOOR 10 +#define RAMP_DISCRETE_CEIL 130 +#define RAMP_DISCRETE_STEPS 7 + +// at Wurkkos's request, reduce the Simple UI ceiling a little bit +// (i.e. not 150; original config had it at 144/150, or DD FET 204/255) +// 10 30 50 70 [90] 110 130 +#define SIMPLE_UI_FLOOR 10 +#define SIMPLE_UI_CEIL 130 +#define SIMPLE_UI_STEPS 7 + +// stop panicking at ~50% power +#define THERM_FASTER_LEVEL 130 // throttle back faster when high + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the aux LEDs by default +#define DEFAULT_BLINK_CHANNEL CM_AUX + +// the default of 26 looks a bit rough, so increase it to make it smoother +#define CANDLE_AMPLITUDE 33 + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + +// enable factory reset on 13H without loosening tailcap (required) +#define USE_SOFT_FACTORY_RESET + diff --git a/hw/wurkkos/ts10/hwdef.c b/hw/wurkkos/ts10/hwdef.c new file mode 100644 index 0000000..06f5bac --- /dev/null +++ b/hw/wurkkos/ts10/hwdef.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/hw/wurkkos/ts10/hwdef.h b/hw/wurkkos/ts10/hwdef.h new file mode 100644 index 0000000..e693c99 --- /dev/null +++ b/hw/wurkkos/ts10/hwdef.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 + +#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/hw/wurkkos/ts11/cfg.h b/hw/wurkkos/ts11/cfg.h new file mode 100644 index 0000000..8955731 --- /dev/null +++ b/hw/wurkkos/ts11/cfg.h @@ -0,0 +1,19 @@ +// Wurkkos TS11, like a TS25 but with a RGB button and RGB front aux +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// ATTINY: 1616 +#include "cfg-wurkkos-ts25.h" +#undef MODEL_NUMBER +#define MODEL_NUMBER "0717" + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// turn on the aux LEDs while main LEDs are on +// (but not until the main LEDs are bright enough to overpower the aux) +// (setting this lower makes an annoying effect on some levels) +#define USE_AUX_RGB_LEDS_WHILE_ON 50 +#define USE_INDICATOR_LED_WHILE_RAMPING + diff --git a/hw/wurkkos/ts25/cfg.h b/hw/wurkkos/ts25/cfg.h new file mode 100644 index 0000000..852e91b --- /dev/null +++ b/hw/wurkkos/ts25/cfg.h @@ -0,0 +1,87 @@ +// Wurkkos TS25, modelled after the TS10 but with RGB Aux +// Copyright (C) 2022-2023 (FIXME) +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define MODEL_NUMBER "0715" +#include "hwdef-wurkkos-ts25.h" +#include "wurkkos-cfg.h" +// ATTINY: 1616 + +// this light has three aux LED channels: R, G, B +#define USE_AUX_RGB_LEDS + +// don't turn on the aux LEDs while main LEDs are on +#ifdef USE_INDICATOR_LED_WHILE_RAMPING +#undef USE_INDICATOR_LED_WHILE_RAMPING +#endif + +// voltage readings were a little high with the Q8 value +#undef VOLTAGE_FUDGE_FACTOR +#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V, not 0.35V + +#define RAMP_SIZE 150 + +#if 0 // 2022 version +// level 1 by hand, for the rest +// level_calc.py 7.01 2 149 7135 3 0.5 125 FET 1 10 1200 --pwm dyn:63:2048:255 +#define PWM1_LEVELS 1,3,3,4,5,6,7,8,9,10,12,13,14,16,17,19,20,22,24,25,27,29,31,33,35,37,40,42,44,47,49,52,54,57,59,62,64,67,70,72,75,77,80,82,85,87,89,91,93,95,96,98,99,100,100,101,100,100,99,97,95,93,90,86,82,87,91,96,100,106,111,116,122,128,134,141,147,155,162,169,177,186,194,203,213,222,232,243,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,6,7,9,11,12,14,16,18,20,22,24,27,29,31,34,37,39,42,45,48,51,54,57,61,64,68,72,75,79,83,88,92,97,101,106,111,116,121,126,132,138,144,150,156,162,169,176,183,190,197,205,213,221,229,237,246,255 +#define PWM_TOPS 2047,2047,1198,1322,1584,1676,1701,1691,1662,1622,1774,1703,1631,1692,1613,1639,1558,1564,1559,1478,1464,1444,1420,1392,1361,1329,1331,1293,1256,1246,1207,1192,1152,1133,1094,1074,1035,1013,991,954,932,897,875,842,820,790,760,731,704,678,646,622,593,566,534,510,478,452,423,393,364,338,310,280,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 +#define MAX_1x7135 90 +#define HALFSPEED_LEVEL 2 +#define QUARTERSPEED_LEVEL 2 +#endif + +// 7135 at 75/150 +// level_calc.py 5.7895 2 150 7135 1 0.1 130 FET 1 10 3000 --pwm dyn:74:4096:255:3 +// (with some manual tweaks) +#define PWM1_LEVELS 1,1,2,3,3,4,5,6,7,8,9,11,12,13,15,16,18,19,21,23,26,27,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,71,74,76,78,80,82,85,87,90,93,96,100,103,107,112,116,122,127,133,140,147,154,163,171,182,192,203,215,228,241,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +// non-zero part of FET channel calculated with: +// level_calc.py 3 1 75 7135 1 200 3000 +// (FIXME? there's a visible bump when the FET kicks in, even with just 1/255) +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,7,8,10,11,13,14,16,17,19,21,22,24,26,28,30,32,34,37,39,41,44,46,48,51,54,56,59,62,65,68,71,74,77,81,84,87,91,94,98,102,106,110,114,118,122,126,130,135,139,144,148,153,158,163,168,173,178,184,189,195,200,206,212,218,224,230,236,242,248,255 +#define PWM_TOPS 4095,2701,3200,3586,2518,2778,2834,2795,2705,2587,2455,2582,2412,2247,2256,2091,2062,1907,1860,1802,1737,1605,1542,1477,1412,1347,1284,1222,1162,1105,1050,997,946,898,853,810,768,730,693,658,625,594,564,536,503,485,462,439,418,398,384,366,353,340,327,319,307,298,292,284,280,273,269,266,263,260,258,256,256,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 + +#define MAX_1x7135 75 +#define DEFAULT_LEVEL 50 +#define MIN_THERM_STEPDOWN 60 +#define HALFSPEED_LEVEL 20 +#define QUARTERSPEED_LEVEL 5 + +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 150 +// 20 38 56 [75] 93 111 130 +#define RAMP_DISCRETE_FLOOR 20 +#define RAMP_DISCRETE_CEIL 130 +#define RAMP_DISCRETE_STEPS 7 + +// at Wurkkos's request, reduce the Simple UI ceiling a little bit +// 25 50 [75] 100 125 +#define SIMPLE_UI_FLOOR 25 +#define SIMPLE_UI_CEIL 125 +#define SIMPLE_UI_STEPS 5 + +// stop panicking at ~50% power +#define THERM_FASTER_LEVEL 120 // throttle back faster when high + +// show each channel while it scroll by in the menu +#define USE_CONFIG_COLORS + +// blink numbers on the main LEDs by default +// (so battcheck will be visible while charging) +#define DEFAULT_BLINK_CHANNEL CM_MAIN +// blink numbers on the aux LEDs by default +//#define DEFAULT_BLINK_CHANNEL CM_AUXWHT + +// use aux red + aux blue for police strobe +#define USE_POLICE_COLOR_STROBE_MODE +#define POLICE_STROBE_USES_AUX +#define POLICE_COLOR_STROBE_CH1 CM_AUXRED +#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU + +// don't blink mid-ramp +#ifdef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_MIDDLE +#endif + diff --git a/hw/wurkkos/ts25/hwdef.c b/hw/wurkkos/ts25/hwdef.c new file mode 100644 index 0000000..26c9b0d --- /dev/null +++ b/hw/wurkkos/ts25/hwdef.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/hw/wurkkos/ts25/hwdef.h b/hw/wurkkos/ts25/hwdef.h new file mode 100644 index 0000000..5eed219 --- /dev/null +++ b/hw/wurkkos/ts25/hwdef.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 + +#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 + diff --git a/hw/wurkkos/wurkkos-cfg.h b/hw/wurkkos/wurkkos-cfg.h new file mode 100644 index 0000000..b9992fb --- /dev/null +++ b/hw/wurkkos/wurkkos-cfg.h @@ -0,0 +1,26 @@ +// Wurkkos config options for Anduril +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// allow Aux Config and Strobe Modes in Simple UI +#define USE_EXTENDED_SIMPLE_UI + +// Allow 3C in Simple UI for switching between smooth and stepped ramping +#define USE_SIMPLE_UI_RAMPING_TOGGLE + +// enable 2 click turbo (Anduril 1 style) +#define DEFAULT_2C_STYLE 1 + +// enable SOS in the blinkies group +#define USE_SOS_MODE +#define USE_SOS_MODE_IN_BLINKY_GROUP + +// enable factory reset on 13H without loosening tailcap +// only really needed on TS10, but doesn't hurt on other models +#define USE_SOFT_FACTORY_RESET + +// higher temperature limit because attiny1616 is more accurate +#undef DEFAULT_THERM_CEIL +#define DEFAULT_THERM_CEIL 50 + diff --git a/hwdef-FET_7135.h b/hwdef-FET_7135.h deleted file mode 100644 index c89d3ce..0000000 --- a/hwdef-FET_7135.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef HWDEF_FET_7135_H -#define HWDEF_FET_7135_H - -/* FET + 7135 driver layout - * ---- - * Reset -|1 8|- VCC - * OTC -|2 7|- Voltage ADC - * Star 3 -|3 6|- PWM (FET) - * GND -|4 5|- PWM (1x7135) - * ---- - */ - -#define STAR2_PIN PB0 // If this pin isn't used for ALT_PWM -#define STAR3_PIN PB4 // pin 3 - -#define CAP_PIN PB3 // pin 2, OTC -#define CAP_CHANNEL 0x03 // MUX 03 corresponds with PB3 (Star 4) -#define CAP_DIDR ADC3D // Digital input disable bit corresponding with PB3 - -#define PWM_PIN PB1 // pin 6, FET PWM -#define PWM_LVL OCR0B // OCR0B is the output compare register for PB1 -#define ALT_PWM_PIN PB0 // pin 5, 1x7135 PWM -#define ALT_PWM_LVL OCR0A // OCR0A is the output compare register for PB0 - -#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 0x06 // clk/64 - -//#define TEMP_DIDR ADC4D -#define TEMP_CHANNEL 0b00001111 - -#define FAST 0xA3 // fast PWM both channels -#define PHASE 0xA1 // phase-correct PWM both channels - -#define LAYOUT_DEFINED - -#endif diff --git a/hwdef-Ferrero_Rocher.h b/hwdef-Ferrero_Rocher.h deleted file mode 100644 index 48519ef..0000000 --- a/hwdef-Ferrero_Rocher.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef HWDEF_FERRERO_ROCHER_H -#define HWDEF_FERRERO_ROCHER_H - -/* Ferrero Rocher driver layout - * ---- - * Reset -|1 8|- VCC - * E-switch -|2 7|- Voltage ADC - * Red LED -|3 6|- PWM - * GND -|4 5|- Green LED - * ---- - */ -// TODO: fill in this section, update Ferrero_Rocher code to use it. -#define FAST 0x23 // fast PWM channel 1 only -#define PHASE 0x21 // phase-correct PWM channel 1 only - -#define LAYOUT_DEFINED - -#endif diff --git a/hwdef-TK_Saber.h b/hwdef-TK_Saber.h deleted file mode 100644 index 8ef58d6..0000000 --- a/hwdef-TK_Saber.h +++ /dev/null @@ -1,37 +0,0 @@ -// TK 4-color lightsaber driver layout -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -/* - * ---- - * Reset -|1 8|- VCC - * PWM 4 (A) -|2 7|- e-switch - * PWM 3 (G) -|3 6|- PWM 2 (B) - * GND -|4 5|- PWM 1 (R) - * ---- - */ - -#define PWM_CHANNELS 4 -#define PWM1_PIN PB0 // pin 5 -#define PWM1_LVL OCR0A -#define PWM2_PIN PB1 // pin 6 -#define PWM2_LVL OCR0B -#define PWM3_PIN PB4 // pin 3 -#define PWM3_LVL OCR1B -#define PWM4_PIN PB3 // pin 2 -#define PWM4_LVL OCR1A - -#define SWITCH_PIN PB2 // pin 7 -#define SWITCH_PCINT PCINT2 // pin 7 pin change interrupt - -#define ADC_PRSCL 0x07 // clk/128 - -// average drop across diode on this hardware -#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V - -#define FAST 0xA3 // fast PWM both channels -#define PHASE 0xA1 // phase-correct PWM both channels - -#define LAYOUT_DEFINED - diff --git a/hwdef-Tripledown.h b/hwdef-Tripledown.h deleted file mode 100644 index 6bde976..0000000 --- a/hwdef-Tripledown.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef HWDEF_TRIPLEDOWN_H -#define HWDEF_TRIPLEDOWN_H - -/* Tripledown driver layout - * ---- - * Reset -|1 8|- VCC - * OTC -|2 7|- Voltage ADC - * PWM (FET) -|3 6|- PWM (6x7135) - * GND -|4 5|- PWM (1x7135) - * ---- - */ - -#define STAR2_PIN PB0 // If this pin isn't used for ALT_PWM - -#define CAP_PIN PB3 // pin 2, OTC -#define CAP_CHANNEL 0x03 // MUX 03 corresponds with PB3 (Star 4) -#define CAP_DIDR ADC3D // Digital input disable bit corresponding with PB3 - -#define PWM_PIN PB1 // pin 6, 6x7135 PWM -#define PWM_LVL OCR0B // OCR0B is the output compare register for PB1 -#define ALT_PWM_PIN PB0 // pin 5, 1x7135 PWM -#define ALT_PWM_LVL OCR0A // OCR0A is the output compare register for PB0 -#define FET_PWM_PIN PB4 // pin 3 -#define FET_PWM_LVL OCR1B // output compare register for PB4 - -#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 0x06 // clk/64 - -//#define TEMP_DIDR ADC4D -#define TEMP_CHANNEL 0b00001111 - -#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 f4a4d7b..0000000 --- a/hwdef-blf-gt-mini.h +++ /dev/null @@ -1,14 +0,0 @@ -// 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 deleted file mode 100644 index fdb0cb6..0000000 --- a/hwdef-blf-gt.h +++ /dev/null @@ -1,102 +0,0 @@ -// 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 - -#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 deleted file mode 100644 index 9d268a4..0000000 --- a/hwdef-blf-lt1-t1616.c +++ /dev/null @@ -1,210 +0,0 @@ -// 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 deleted file mode 100644 index 7c1f10b..0000000 --- a/hwdef-blf-lt1-t1616.h +++ /dev/null @@ -1,161 +0,0 @@ -// 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 - -#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 deleted file mode 100644 index 8a4c0e1..0000000 --- a/hwdef-blf-lt1.c +++ /dev/null @@ -1,204 +0,0 @@ -// 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 deleted file mode 100644 index b113fd4..0000000 --- a/hwdef-blf-lt1.h +++ /dev/null @@ -1,114 +0,0 @@ -// 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 - -#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< 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 deleted file mode 100644 index d9b981f..0000000 --- a/hwdef-blf-q8-t1616.h +++ /dev/null @@ -1,138 +0,0 @@ -// 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 - -// 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 deleted file mode 100644 index cdf311d..0000000 --- a/hwdef-blf-q8.h +++ /dev/null @@ -1,20 +0,0 @@ -// BLF Q8 driver layout -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// Q8 driver is the same as a D4, basically - -// ... except the Q8 has a lighted button -#ifndef AUXLED_PIN -#define AUXLED_PIN PB4 // pin 3 -#endif - -// ... and slightly different calibration -#ifndef VOLTAGE_FUDGE_FACTOR -#define VOLTAGE_FUDGE_FACTOR 7 // add 0.35V -#endif - -// Q8 driver is the same as a D4, basically -#include "hwdef-emisar-d4.h" - diff --git a/hwdef-emisar-2ch-fet.c b/hwdef-emisar-2ch-fet.c deleted file mode 100644 index caf579d..0000000 --- a/hwdef-emisar-2ch-fet.c +++ /dev/null @@ -1,206 +0,0 @@ -// 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 deleted file mode 100644 index d756a0d..0000000 --- a/hwdef-emisar-2ch-fet.h +++ /dev/null @@ -1,209 +0,0 @@ -// 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 - -#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<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 deleted file mode 100644 index 99e4945..0000000 --- a/hwdef-emisar-2ch.h +++ /dev/null @@ -1,210 +0,0 @@ -// 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 - -#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< - -#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 deleted file mode 100644 index 972f682..0000000 --- a/hwdef-emisar-d4.c +++ /dev/null @@ -1,59 +0,0 @@ -// 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 deleted file mode 100644 index 7be700a..0000000 --- a/hwdef-emisar-d4.h +++ /dev/null @@ -1,104 +0,0 @@ -// 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 - -#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 deleted file mode 100644 index e35af08..0000000 --- a/hwdef-emisar-d4k-3ch.c +++ /dev/null @@ -1,362 +0,0 @@ -// 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 deleted file mode 100644 index 2e83fbe..0000000 --- a/hwdef-emisar-d4k-3ch.h +++ /dev/null @@ -1,248 +0,0 @@ -// 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 - -#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< (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 deleted file mode 100644 index 11d1abb..0000000 --- a/hwdef-emisar-d4sv2.h +++ /dev/null @@ -1,179 +0,0 @@ -// 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 - -#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< (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 deleted file mode 100644 index 026b30d..0000000 --- a/hwdef-emisar-d4v2.c +++ /dev/null @@ -1,61 +0,0 @@ -// 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 deleted file mode 100644 index 96c57a9..0000000 --- a/hwdef-emisar-d4v2.h +++ /dev/null @@ -1,171 +0,0 @@ -// 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 - -#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< - -#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 deleted file mode 100644 index 2f31ed0..0000000 --- a/hwdef-fw3x-lume1.c +++ /dev/null @@ -1,60 +0,0 @@ -// 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 deleted file mode 100644 index c03248b..0000000 --- a/hwdef-fw3x-lume1.h +++ /dev/null @@ -1,202 +0,0 @@ -// 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 - * 5 PA2 e-switch (PCINT2) - * 6 PA1 Jumper 1 - * 7 PA0 Jumper 2 - * 8 GND GND - * 9 VCC VCC - * 10 PC5 Jumper 3 - * 11 PC4 Jumper 4 - * 12 PC3 RESET - * 13 PC2 External Thermal Sensor (ADC11) - * 14 PC1 SCK - * 15 PC0 N/C - * 16 PB3 FET PWM (PWM1A) - * 17 PB2 MISO - * 18 PB1 MOSI - * 19 PB0 ADC5 Voltage Battery Sense (2:1 divider) - * 20 PA7 BB_Enable - * 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 DD FET control. - */ - -#define ATTINY 1634 -#include - -#define HWDEF_C_FILE hwdef-fw3x-lume1.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 2 // old, remove this - -// Added for Lume1 Buck Boost Driver -#define PWM_BITS 16 // 0 to 1023 at 3.9 kHz, not 0 to 255 at 15.6 kHz -#define PWM_GET PWM_GET16 -#define PWM_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 -#define JUMPER2_PIN PA0 -#define JUMPER3_PIN PC5 -#define JUMPER4_PIN PC4 -*/ - -// 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 -#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC5D -// 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, 0, 1 for ADC5 / PB0 -// divided by ... -// REFS[1:0] = 0, 0 for VCC as analog reference at 2.5V -// other bits reserved -#define ADMUX_VOLTAGE_DIVIDER 0b00000101 -#define ADC_PRSCL 0x06 // clk/64 - -// Raw ADC readings at 4.4V and 2.2V -// calibrate the voltage readout here -// estimated / calculated values are: -// [(Vbatt)*(R2/(R2+R1)) / 2.5V] * 1023 -// R1 = R2 = 100kR -#ifndef ADC_44 -#define ADC_44 900 -#endif -#ifndef ADC_22 -#define ADC_22 450 -#endif - -// Default ADMUX_THERM for Temperature is: 0b10001110 in tk-attiny.h -// REFS[1:0] as 10 for analog reference at internal 1.1Vref -// MUX[3:0] as 1110 for ADC'12' - temperature sensor internal - -// Modified fsm-adc.c to use different ADMUX and ADC_temperature_handler() -// based on USE_EXTERNAL_TEMP_SENSOR -// See line 34 and line 209 -#define USE_EXTERNAL_TEMP_SENSOR -#define ADMUX_THERM_EXTERNAL_SENSOR 0b00001011 // VCC reference (2.5V), Channel PC2 -// Used for Lume1 Driver: MCP9700 - T_Celsius = 100*(VOUT - 0.5V) -// ADC is 2.5V reference, 0 to 1023 -// FIXME: due to floating point, this calculation takes 916 extra bytes -// (should use an integer equivalent instead) -#define EXTERN_TEMP_FORMULA(m) (((m)-205)/4.09) - -// this driver allows for aux LEDs under the optic -#define AUXLED_R_PIN PA5 // pin 2 -#define AUXLED_G_PIN PA4 // pin 3 -#define AUXLED_B_PIN PA3 // pin 4 -#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 - -// For lume1 driver, no SW support for Auxillary Jumpers X1 to X4 yet! -inline void hwdef_setup() { - // 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< - -// 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 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 -#define AUXLED_PIN PIN3_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 = 0b00000010; - // 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 - 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; - PORTA.PIN7CTRL = PORT_PULLUPEN_bm; - - //PORTB.PIN0CTRL = PORT_PULLUPEN_bm; // FET channel - //PORTB.PIN1CTRL = PORT_PULLUPEN_bm; // 7135 channel - PORTB.PIN2CTRL = PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; // switch - //PORTB.PIN3CTRL = PORT_PULLUPEN_bm; // Aux LED - PORTB.PIN4CTRL = PORT_PULLUPEN_bm; - PORTB.PIN5CTRL = PORT_PULLUPEN_bm; - - 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-mateminco-mf01-mini.h b/hwdef-mateminco-mf01-mini.h deleted file mode 100644 index 35405db..0000000 --- a/hwdef-mateminco-mf01-mini.h +++ /dev/null @@ -1,100 +0,0 @@ -// 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 - -#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 deleted file mode 100644 index af214b2..0000000 --- a/hwdef-mateminco-mf01s.h +++ /dev/null @@ -1,102 +0,0 @@ -// 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 - -#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 deleted file mode 100644 index 015c813..0000000 --- a/hwdef-mateminco-mt35-mini.h +++ /dev/null @@ -1,93 +0,0 @@ -// 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 - -#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-nanjg.h b/hwdef-nanjg.h deleted file mode 100644 index 135edbc..0000000 --- a/hwdef-nanjg.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef HWDEF_NANJG_H -#define HWDEF_NANJG_H - -/* NANJG driver layout - */ -#define STAR2_PIN PB0 -#define STAR3_PIN PB4 -#define STAR4_PIN PB3 -#define PWM_PIN PB1 -#define VOLTAGE_PIN PB2 -#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2 -#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2 -#define ADC_PRSCL 0x06 // clk/64 - -#define PWM_LVL OCR0B // OCR0B is the output compare register for PB1 - -#define FAST 0x23 // fast PWM channel 1 only -#define PHASE 0x21 // phase-correct PWM channel 1 only - -#define LAYOUT_DEFINED - -#endif diff --git a/hwdef-noctigon-dm11-boost.c b/hwdef-noctigon-dm11-boost.c deleted file mode 100644 index 932323a..0000000 --- a/hwdef-noctigon-dm11-boost.c +++ /dev/null @@ -1,97 +0,0 @@ -// 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 deleted file mode 100644 index d56a5f5..0000000 --- a/hwdef-noctigon-dm11-boost.h +++ /dev/null @@ -1,206 +0,0 @@ -// 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 - -#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< - -#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< - -#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< - -#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< (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 deleted file mode 100644 index 6467567..0000000 --- a/hwdef-noctigon-k1.h +++ /dev/null @@ -1,170 +0,0 @@ -// 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 - -#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< - -// move the switch to a different pin -#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 rest of the config is the same as the generic Emisar 2ch build -#include "hwdef-emisar-2ch.h" - diff --git a/hwdef-noctigon-kr4-boost.h b/hwdef-noctigon-kr4-boost.h deleted file mode 100644 index 07c4f4b..0000000 --- a/hwdef-noctigon-kr4-boost.h +++ /dev/null @@ -1,57 +0,0 @@ -// 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 deleted file mode 100644 index 0492def..0000000 --- a/hwdef-noctigon-kr4-nofet.c +++ /dev/null @@ -1,60 +0,0 @@ -// 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 deleted file mode 100644 index 884151d..0000000 --- a/hwdef-noctigon-kr4.c +++ /dev/null @@ -1,63 +0,0 @@ -// 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 deleted file mode 100644 index 5570fb7..0000000 --- a/hwdef-noctigon-kr4.h +++ /dev/null @@ -1,194 +0,0 @@ -// 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 - -#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<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<> 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 deleted file mode 100644 index 5658c9f..0000000 --- a/hwdef-noctigon-m44.h +++ /dev/null @@ -1,206 +0,0 @@ -// 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 - -#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< 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 deleted file mode 100644 index ae6b3bf..0000000 --- a/hwdef-sofirn-lt1s-pro.h +++ /dev/null @@ -1,151 +0,0 @@ -// 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 - -#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 deleted file mode 100644 index 42844a7..0000000 --- a/hwdef-sofirn-sp10-pro.c +++ /dev/null @@ -1,63 +0,0 @@ -// 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 deleted file mode 100644 index 5cef6a7..0000000 --- a/hwdef-sofirn-sp10-pro.h +++ /dev/null @@ -1,157 +0,0 @@ -// 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 - */ - -#define ATTINY 1616 -#include - -#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 - -// 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 // 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 -#define CH1_PIN PB5 -#define CH1_PWM TCA0.SINGLE.CMP2BUF // PB5 is Alternate MUX for TCA Compare 2 - -// Big channel -#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 - -// Raw ADC readings at 4.4V and 2.2V -// calibrate the voltage readout here -// estimated / calculated values are: -// (voltage - D1) * (R2/(R2+R1) * 1024 / 1.1) -// Resistors are 300,000 and 100,000 -#ifndef ADC_44 -#define ADC_44 1023 // raw value at 4.40V -#endif -#ifndef ADC_22 -#define ADC_22 512 // raw value at 2.20V -#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 = PIN1_bm; // Boost enable pin - VPORTB.DIR = PIN0_bm // big PWM channel - | PIN5_bm; // small PWM channel - //VPORTC.DIR = ...; - - // enable pullups on the input pins to reduce power - PORTA.PIN0CTRL = PORT_PULLUPEN_bm; - //PORTA.PIN1CTRL = PORT_PULLUPEN_bm; // Boost enable pin - 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; - PORTA.PIN7CTRL = PORT_PULLUPEN_bm; - - //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; // e-switch - //PORTB.PIN4CTRL = PORT_PULLUPEN_bm; // Voltage divider - //PORTB.PIN5CTRL = PORT_PULLUPEN_bm; // Small PWM channel - - 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 - 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; - PWM_TOP = PWM_TOP_INIT; - TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc - | TCA_SINGLE_ENABLE_bm; -} - - -// set fuses, these carry over to the ELF file -// we need this for enabling BOD in Active Mode from the factory. -// settings can be verified / dumped from the ELF file using this -// command: avr-objdump -d -S -j .fuse anduril.elf -FUSES = { - .WDTCFG = FUSE_WDTCFG_DEFAULT, // Watchdog Configuration - .BODCFG = FUSE_ACTIVE0, // BOD Configuration - .OSCCFG = FUSE_OSCCFG_DEFAULT, // Oscillator Configuration - .TCD0CFG = FUSE_TCD0CFG_DEFAULT, // TCD0 Configuration - .SYSCFG0 = FUSE_SYSCFG0_DEFAULT, // System Configuration 0 - .SYSCFG1 = FUSE_SYSCFG1_DEFAULT, // System Configuration 1 - .APPEND = FUSE_APPEND_DEFAULT, // Application Code Section End - .BOOTEND = FUSE_BOOTEND_DEFAULT, // Boot Section End -}; - - -#define LAYOUT_DEFINED - diff --git a/hwdef-thefreeman-boost-fwaa-mp3432-hdr-dac-rgb.h b/hwdef-thefreeman-boost-fwaa-mp3432-hdr-dac-rgb.h deleted file mode 100755 index 9126a1a..0000000 --- a/hwdef-thefreeman-boost-fwaa-mp3432-hdr-dac-rgb.h +++ /dev/null @@ -1,187 +0,0 @@ -// 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 - -#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 deleted file mode 100644 index 31feec9..0000000 --- a/hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.c +++ /dev/null @@ -1,90 +0,0 @@ -// 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 deleted file mode 100644 index 1c35014..0000000 --- a/hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.h +++ /dev/null @@ -1,181 +0,0 @@ -// 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 - -#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 deleted file mode 100644 index 07c7f9e..0000000 --- a/hwdef-thefreeman-lin16dac.c +++ /dev/null @@ -1,92 +0,0 @@ -// 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 deleted file mode 100644 index c0d0638..0000000 --- a/hwdef-thefreeman-lin16dac.h +++ /dev/null @@ -1,137 +0,0 @@ -// 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 - */ - -#define ATTINY 1616 -#include - -#define HWDEF_C_FILE hwdef-thefreeman-lin16dac.c - -// allow using aux LEDs as extra channel modes -#include "chan-aux.h" - -// channel modes: -// * 0. main LEDs -// * 1+. aux RGB -#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 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 - -// Opamp enable -// For turning on and off the op-amp -#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 -#define VOLTAGE_FUDGE_FACTOR 0 // using a PFET so no appreciable drop -#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 - // (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 - 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 ouput - //PORTA.PIN7CTRL = PORT_PULLUPEN_bm; // Op-amp enable pin - - PORTB.PIN0CTRL = PORT_PULLUPEN_bm; - PORTB.PIN1CTRL = 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; // e-switch - //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 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-wurkkos-ts10.c b/hwdef-wurkkos-ts10.c deleted file mode 100644 index 06f5bac..0000000 --- a/hwdef-wurkkos-ts10.c +++ /dev/null @@ -1,65 +0,0 @@ -// 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 deleted file mode 100644 index e693c99..0000000 --- a/hwdef-wurkkos-ts10.h +++ /dev/null @@ -1,136 +0,0 @@ -// 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 - -#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 deleted file mode 100644 index 26c9b0d..0000000 --- a/hwdef-wurkkos-ts25.c +++ /dev/null @@ -1,62 +0,0 @@ -// 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 deleted file mode 100644 index 5eed219..0000000 --- a/hwdef-wurkkos-ts25.h +++ /dev/null @@ -1,144 +0,0 @@ -// 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 - -#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 - diff --git a/spaghetti-monster/COPYING b/spaghetti-monster/COPYING deleted file mode 100644 index 94a9ed0..0000000 --- a/spaghetti-monster/COPYING +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/spaghetti-monster/anduril/BRANDS b/spaghetti-monster/anduril/BRANDS deleted file mode 100644 index 059f311..0000000 --- a/spaghetti-monster/anduril/BRANDS +++ /dev/null @@ -1,11 +0,0 @@ -Vendor / Model IDs for version check function - -Undefined 0000 -Emisar 0100 - 0199 -Noctigon 0200 - 0199 -Lumintop 0300 - 0399 -Fireflies 0400 - 0499 -Mateminco 0500 - 0599 -Sofirn 0600 - 0699 -Wurkkos 0700 - 0799 -gChart 1600 - 1699 diff --git a/spaghetti-monster/anduril/ChangeLog.md b/spaghetti-monster/anduril/ChangeLog.md deleted file mode 100644 index 0aaa392..0000000 --- a/spaghetti-monster/anduril/ChangeLog.md +++ /dev/null @@ -1,245 +0,0 @@ -# ChangeLog - -Items should be sorted roughly in order of most to least important, with -model-specific changes at the bottom. Most items should use one of these -formats: - - - Breaking change: - - Added - - Fixed - - Removed - - Changed - - Documented - - @brand-model: Hardware-specific change (NNNN, NNNN, ...) - -# Next - -# 2023-10-31 - -General: - -- Finally finished converting all lights to the multi-channel API. - (but a few are untested due to hardware being unavailable) -- Enabled smooth steps on almost all supported lights, including old ones. - (in some cases, on old t85 lights, some other things may have been removed - to make room) -- Made smooth steps work better, and fixed several bugs. -- Added/fixed RGB aux voltage on dual-fuel AA/li-ion drivers. -- Fixed delay timing on all supported MCUs, so 1 "second" is closer to an - actual second (like in beacon mode or biking mode). - (especially attiny1616, which was 17% too slow) -- Raised default thermal limit to 50C on attiny1616-based lights, since - it doesn't need as big an error margin as older MCUs. -- Fixed missing item in globals menu on some lights. -- Misc internal improvements and per-light tweaks. -- Added a ChangeLog.md, to periodically summarize changes in a format which is - more readable than the raw commit logs. - -New lights: - -- @thefreeman-boost21-6a: Added. (1631) - (very nice HDR boost driver which fits into a FW3A) -- @thefreeman-boost-fwaa: Added. (1632) - (very nice AA/li-ion HDR boost driver which fits into a FWAA) - -Hardware-specific changes: - -- Upgraded several builds to use delta-sigma modulation (DSM), for - lower lows and smoother ramping with less flicker or ripple: - - @blf-lt1 (0621) - - @blf-lt1-t1616 (0622) - - @emisar-d4k-3ch (0151) - (**dramatically** improves resolution and low modes on its 8-bit channel) - - @noctigon-dm11-boost (0273) - - @noctigon-kr4-boost (0216) - - @noctigon-k1-boost (0253) - - @noctigon-m44 (0143) - -- Upgraded some builds to use dynamic PWM, for lower lows and smoother ramping: - - @blf-q8-t1616, @sofirn-sp36-t1616 (0613, 0614) - - @gchart-fet1-t1616 (1618) - - @noctigon-k1-sbt90 (0252) - -- @wurkkos-ts10, @wurkkos-ts10-rgbaux: Fixed too-high default ceiling. (0713, 0714) - -# 2023-10-01 - -General: - -- Updated a bunch of lights to work with the new multi-channel API. -- Added "smooth steps" a.k.a. "soft start", to make on/off and step changes - smooth. (enabled by default, can be turned off in Ramp Extras menu) -- User can save a different channel mode for each strobe-group mode. -- Made strobes/blinkies more responsive in Tactical Mode. -- Fixed bug: Tactical Mode clobbers strobe group memory. -- Fixed some minor post-off voltage display bugs. -- Made RGB button brightness update faster in blinky modes. -- Fixed bug: Wrong channel after colored factory reset. -- @attiny1616, @attiny1634: Partially fixed oscillating aux LED voltage - colors while off. Better than before, but can still happen in some cases. -- @attiny1616: Fixed spurious voltage warnings in sleep mode. (it could - sometimes go from Lockout mode to Off mode by itself) -- Lots of internal refactoring. - -New lights: - -- @emisar-2ch-fet: Added. (0136) -- @emisar-d4k-3ch: Added. (0151) -- @noctigon-m44: Added. (0143) -- @wurkkos-ts10-rgbaux: Added. (0713) - -Hardware-specific changes: - -- @ff-e01, @ff-pl47, @ff-pl47g2: Enabled smooth steps instead of SOS mode. - (0421, 0422, 0423, 0441) -- @emisar-2ch, @noctigon-m44: Added RGB aux channel modes. (0135, 0143) -- @emisar-2ch-fet, @noctigon-k9.3: New ramps with better-calibrated shape. - (0136, 0261) -- @emisar-d4v2-nofet: New ramp table. (0115) -- @emisar-d4sv2-tintramp: Removed / renamed. (0135, 0136) -- @noctigon-k9.3: Fixed years-old mess. Merged builds, converted to - multi-channel, removed old builds, generally got K9.3 working quite a bit - better. (0261, 0262, 0263, 0265, 0266, 0267) -- @noctigon-m44: Lower moon, and greatly reduced flicker. (0143) -- @sofirn-lt1s-pro: Allow configuring the blink channel. (0623) -- @wurkkos: Raised default temperature limit to 50 C. (07xx) -- @wurkkos-ts10: Better / smoother ramp. (0713, 0714) - -# 2023-06-29 - -- Fixed red aux blink on 1st frame of post-off voltage display -- Removed Harry Potter references because its author (J.K. Rowling) spreads - hate -- @noctigon-kr4: Fixed thermal regulation (0211, 0212, 0213, 0214, 0215, 0216) - -# 2023-05-30 - -- Breaking change: Changed version check from YYYYMMDDXXXX to XXXX.YYYY-MM-DD, - where the punctuation makes a "buzz" instead of number blinks. -- Fixed issue where some lights always thought they were overheating (when - cold, or after a factory reset) -- Fixed aux pre-flash -- Fixed a rare corner case where gradual brightness adjustments stopped at 99% - power instead of 100% power while going up from level 149/150 to 150/150 on - some lights -- Changed "0" digit readout to use the same timing as other short blinks, to - help ensure it works on all lights. -- Minor code changes with no runtime impact -- Documented new version check format -- @wurkkos: Added red+blue police strobe (0715, 0716, 0717) -- @noctigon-kr4: Broke thermal regulation (oops) (0211, 0212, 0213, 0214, - 0215, 0216) -- @noctigon-kr4: Use 7 aux channel modes instead of 3 (0211, 0212, 0213, 0214, - 0215, 0216) -- @emisar-d4v2: Changed number blinks from aux to main LEDs by default (0113, - 0114, 0115, 0123) - -# 2023-05-17 - -- @noctigon-dm11-12v: Renamed to noctigon-dm11-boost (0273) -- @noctigon-dm11-boost: Now supported in multi-channel branch (0273) - -# 2023-05-02 - -- Added ability to set channel mode for number readouts (batt check, temp - check, version check). Press 3C in batt check mode to change the blink - channel. -- Changed post-off voltage display: - - Display for 4s by default, not 5s - - Made timing user-configurable in battcheck 7H menu item 2 - (1 click per second the display should last) - - Use low brightness when torch was at moon level before - - Skip the voltage display after UI actions which didn't change the mode - (like "Off -> 7C" to change aux LED settings) -- Changed RGB button behavior: While torch is on, use low mode below a - configured level for real-time voltage color display, instead of always - using high mode -- Fixed bug: Channel change could stick when activating a config menu from - battcheck (the blink function changed the channel, then the config menu - saved it, then the blink function restored it, then the config menu restored - it to the value it saved, which was wrong) -- Documented ramp 6C, ramp 4C, ramp 4H, lockout 3H, battcheck 3C, and post-off - voltage display config -- @emisar-d4v2: Added the rest of the aux RGB colors as channel modes, and set - aux "white" as the mode it uses to blink out numbers (0113, 0114, 0115, - 0123) -- @wurkkos-ts10: Converted to multi-channel, and gave it a new ramp with - better low modes (0714) -- @wurkkos-ts25: Converted to multi-channel, and gave it a smoother ramp - (0715) -- @wurkkos: Added Wurkkos FC13 and TS11 (0716, 0717) - -# 2023-04-29 - -- Changed lockout mode: - - Fixed manual memory timer and tint - - Added 3H for next channel (to change channels without unlocking, was - possible before but needed to be updated for the new channel system) - - Made 3H+ use mem level instead of lowest moon (this is needed for - making the channel discernible, and also helps make aux LED controls - stand out more) -- @emisar, @noctigon: Added Extended Simple UI to Hank's config, so a few more - features are allowed in simple mode -- @emisar-d4v2, @noctigon-kr4: Slightly smaller ROM size -- @emisar-d4sv2: Converted to multi-channel, and updated it to use dynamic PWM - for a smoother ramp with lower lows (0133, 0134) -- @noctigon-kr4: Converted to multi-channel (0211, 0212, 0213, 0214) -- @noctigon-kr4: Don't blink at top of regulated power (0211, 0213, 0214) - -# 2023-04-28 - -- Added voltage display (by color) on RGB aux LEDs after turning the main LEDs - off -- Changed RGB aux: Always preview in high mode while changing color or pattern -- Changed default RGB aux color in lockout mode: Uses blinking voltage, - instead of blinking disco (unless model-specific config defines a different - default) -- Changed tactical mode default config: only use 2-color strobe if it's on - main LEDs, not aux LEDs -- @emisar-d4v2: Smoother ramp (0113, 0114) -- @emisar-d4v2: Added hidden channel modes for RGB aux LEDs (0113, 0114, 0115) - -# 2023-04-27 - -- Changed channel mode menu to preview channels during configuration -- Changed tactical mode default config: use 2-color police strobe if it - exists, instead of single-color strobe -- Fixed unnecessary flickering when changing channel modes from/to the same - value -- Fixed sleep voltage measurement on attiny1616 -- @noctigon-kr4-tintramp: Converted to multi-channel, renamed to - noctigon-kr4-2ch (0215) - -# 2023-04-25 - -- Fixed old old bug(s) with voltage measurement while asleep, while also - adding other improvements: - - Fixed oscillating RGB aux colors while asleep in voltage mode, mostly. - Some much smaller oscillations are still possible, but the primary issue - has been fixed, I think. - - Fixed old old bug which could cause spurious low-voltage warnings while - asleep (previously required a messy code workaround). - - Reduced avg standby power by ~15uA. - - Now measures voltage every 1s instead of 8s while asleep. - - Changed RGB aux behavior: Puts aux LEDs in high mode for 3 seconds after - light goes to sleep (much easier to see the voltage this way) - (this change was obsoleted soon by a better post-off voltage display) - - Broke sleep voltage measurement on attiny1616 (oops) -- Changed internal details for how gradual ramping works -- @emisar-d4sv2-tintramp: Converted to multi-channel, renamed to emisar-2ch. - (0135) -- @sofirn-lt1s-pro: Updated to use today's new code internals (0623) - -# 2023-04-19 - -- Added stepped tint ramping -- Documented new channel modes system -- @sofirn-lt1s-pro: Added white-only auto-tint mode (0623) - -# Older: TODO - -- Need to import old commit logs as changelog items - - - - diff --git a/spaghetti-monster/anduril/MODELS b/spaghetti-monster/anduril/MODELS deleted file mode 100644 index f4e9fdf..0000000 --- a/spaghetti-monster/anduril/MODELS +++ /dev/null @@ -1,85 +0,0 @@ -Model Name MCU ------ ---- --- -0111 emisar-d4 attiny85 -0112 emisar-d4-219c attiny85 -0113 emisar-d4v2 attiny1634 -0114 emisar-d4v2-219 attiny1634 -0115 emisar-d4v2-nofet attiny1634 -0121 emisar-d1 attiny85 -0122 emisar-d1s attiny85 -0123 emisar-d1v2-7135-fet attiny1634 -0124 emisar-d1v2-linear-fet attiny1634 -0125 emisar-d1v2-nofet attiny1634 -0131 emisar-d4s attiny85 -0132 emisar-d4s-219c attiny85 -0133 emisar-d4sv2 attiny1634 -0134 emisar-d4sv2-219 attiny1634 -0135 emisar-2ch attiny1634 -0135 emisar-d4sv2-tintramp attiny1634 (old) -0136 emisar-2ch-fet attiny1634 -0136 emisar-d4sv2-tintramp-fet attiny1634 (old) -0141 emisar-d18 attiny85 -0142 emisar-d18-219 attiny85 -0143 noctigon-m44 attiny1634 -0151 emisar-d4k-3ch attiny1634 -0211 noctigon-kr4 attiny1634 -0212 noctigon-kr4-nofet attiny1634 -0213 noctigon-kr4-219 attiny1634 -0214 noctigon-kr4-219b attiny1634 -0215 noctigon-kr4-2ch attiny1634 -0215 noctigon-kr4-tintramp attiny1634 (old) -0216 noctigon-kr4-boost attiny1634 -0216 noctigon-kr4-12v attiny1634 (old) -0251 noctigon-k1 attiny1634 -0252 noctigon-k1-sbt90 attiny1634 -0253 noctigon-k1-boost attiny1634 -0253 noctigon-k1-12v attiny1634 (old) -0261 noctigon-k9.3 attiny1634 -0262 noctigon-k9.3-nofet attiny1634 -0263 noctigon-k9.3-219 attiny1634 -0265 noctigon-k9.3-tintramp-nofet attiny1634 (old, use 0262) -0266 noctigon-k9.3-tintramp-fet attiny1634 (old, use 0261) -0267 noctigon-k9.3-tintramp-219 attiny1634 (old, use 0263) -0271 noctigon-dm11 attiny1634 -0272 noctigon-dm11-nofet attiny1634 -0273 noctigon-dm11-boost attiny1634 -0273 noctigon-dm11-12v attiny1634 (old) -0274 noctigon-dm11-sbt90 attiny1634 -0311 fw3a attiny85 -0312 fw3a-219 attiny85 -0313 fw3a-nofet attiny85 -0314 fw3x-lume1 attiny1634 -0321 blf-gt attiny85 -0322 blf-gt-mini attiny85 -0411 ff-rot66 attiny85 -0412 ff-rot66-219 attiny85 -0413 ff-rot66g2 attiny85 -0421 ff-pl47 attiny85 -0422 ff-pl47-219 attiny85 -0423 ff-pl47g2 attiny85 -0441 ff-e01 attiny85 -0511 mateminco-mf01s attiny85 -0521 mateminco-mf01-mini attiny85 -0531 mateminco-mt35-mini attiny85 -0611 blf-q8 attiny85 -0612 sofirn-sp36 attiny85 -0613 blf-q8-t1616 attiny1616 -0614 sofirn-sp36-t1616 attiny1616 -0621 blf-lantern attiny85 -0622 blf-lantern-t1616 attiny1616 -0623 sofirn-lt1s-pro attiny1616 -0631 sofirn-sp10-pro attiny1616 -0632 sofirn-sc21-pro attiny1616 -0713 wurkkos-ts10-rgbaux attiny1616 -0714 wurkkos-ts10 attiny1616 -0715 wurkkos-ts25 attiny1616 -0716 wurkkos-fc13 attiny1616 -0717 wurkkos-ts11 attiny1616 -1618 gchart-fet1-t1616 attiny1616 -1630 thefreeman-lin16dac attiny1616 -1631 thefreeman-boost21-6a attiny1616 -1632 thefreeman-boost-fwaa attiny1616 - -Duplicates: - -Missing: diff --git a/spaghetti-monster/anduril/Makefile b/spaghetti-monster/anduril/Makefile deleted file mode 100644 index 332f0f3..0000000 --- a/spaghetti-monster/anduril/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -all: - ./build-all.sh - -clean: - rm -f *.hex *~ *.elf *.o - -todo: - @egrep 'TODO:|FIXME:' *.[ch] - -models: - @./models.py > MODELS - @cat MODELS - -.phony: clean todo diff --git a/spaghetti-monster/anduril/anduril-manual.txt b/spaghetti-monster/anduril/anduril-manual.txt deleted file mode 100644 index 545af0f..0000000 --- a/spaghetti-monster/anduril/anduril-manual.txt +++ /dev/null @@ -1,994 +0,0 @@ -Anduril User Manual -------------------- - -Anduril is an open-source firmware for flashlights, distributed under -the terms of the GPL v3. The sources can be obtained here: - - http://tiny.cc/TKAnduril - https://bazaar.launchpad.net/~toykeeper/flashlight-firmware/trunk/files/head:/ToyKeeper/spaghetti-monster/anduril/ - - -Quick Start ------------ - -After putting a battery in the light and tightening the parts together, -the light should quickly blink once to confirm it has power and is now -operational. After that, basic usage is simple: - - - Click to turn the light on or off. - - Hold the button to change brightness. - - Release and hold again to change brightness the other way. - -That is all the user needs to know for basic use, but there are many -more modes and features available for people who want more. - -Before reading the rest of this manual, it is recommended that users -look at the Anduril UI diagram(s), which should be provided along with the -flashlight. - - -Button presses --------------- - -Button presses are abbreviated using a simple notation: - - - 1C: One click. Press and then quickly release the button. - - 1H: Hold. Press the button, but keep holding it. - - 2C: Two clicks. Press and release quickly, twice. - - 2H: Click, hold. Click two times, but hold the second press. - - 3C: Three clicks. Press and release quickly, three times. - - 3H: Click, click, hold. Click three times, but hold the final press. - -The same pattern is used with higher numbers too. For example, 10C -means ten clicks... and 10H means ten clicks but hold the final press. - - -Simple UI ---------- - -By default, the light uses a simple UI. This is useful if you lend the -light to someone else, or if you just don't want to bother with any -crazy disco modes. - -Simple UI has all the basic functions needed to work as a flashlight, -but the minimum and maximum brightness are limited by default to make it -safer, and any complex or advanced functions are blocked. - -Functions available in Simple UI include: - - - 1C: On / off - - 1H: Ramp up (or down, if button was released less than a second ago) - - 2H: If light is on : ramp down - If light is off: momentary high mode - - 2C: Double click to go to / from highest safe level - - 4C: Lockout mode. - -Some other modes and functions are available too. When the light is -off, these are the options: - - - 3C: Battery check mode. (displays voltage once, then turns off) - - 4C: Lockout mode. - - 10H: Switch to Advanced UI. - - 15C or more: Version check. - -In Lockout mode with Simple UI, there are a few functions: - - - 1H: Momentary moon - - 2H: Momentary low - - 3C: Unlock and turn off - - 4C: Unlock and turn on - - 4H: Unlock and turn on at low level - - 5C: Unlock and turn on at high level - -To change between Simple UI and Advanced UI, turn the light off and then -do one of these: - - In Simple UI: - - 10H: Go to Advanced UI. - - In Advanced UI: - - 10C: Go to Simple UI. - - 10H: Configure Simple UI. - -If you get lost, or if you want to auto-calibrate the temperature -sensor, do a factory reset. The process for this is: - - - Loosen tailcap - - Hold button - - Tighten tailcap - - Keep holding button for about 4s - -The light should flicker while getting brighter, then briefly burst to -full power. Hold until it reaches full power to do a reset, or let go -of the button early to abort. - -On some lights, like products where the tailcap method is impossible, -use 13H from Off to do a factory reset. If this is difficult, try -counting it like music to make it easier: - - 1 2 3 4 - 1 2 3 4 - 1 2 3 4 - HOLD - -Simple UI is enabled after each factory reset. - -Simple UI can be configured in several ways, but not while Simple UI is -active. So go to the Advanced UI, configure things, then go back to -Simple UI. - -Configurable options include: - - - floor level - - ceiling level - - number of steps (in stepped ramp) - - turbo style - -Other options are inherited from Advanced UI: - - - ramp style (smooth / stepped) - - smooth ramp speed - - ramp-after-moon style - - memory settings - - auto-lock settings - - aux LED settings - - voltage calibration - - thermal regulation settings - - hardware-specific "misc menu" settings - - -Advanced UI ------------ - -Most of the information below this is for the Advanced UI. Anything not -already noted above is blocked in the Simple UI. - - -Ramping / Stepped Ramping Modes -------------------------------- - -Anduril's ramping mode uses a smooth ramp or a stepped ramp, depending -on which style the user prefers. - -Each ramp has its own settings -- floor (lowest level), ceiling (highest -level), and the stepped ramp can also have a configurable number of -steps. - -Additionally, Simple UI has its own ramp settings for floor, ceiling, -and number of steps. The smooth/stepped style is inherited from the -Advanced UI's ramp. - -There are four ways to access ramping mode when the light is off: - - - 1C: Turn on at the memorized brightness. - (see below for details about what "memorized" means) - - - 1H: Turn on at the floor level. Let go after the light turns on to - stay at the floor level, or keep holding to ramp up. - - - 2C: Turn on at the ceiling level. - - - 2H: Turn on at full power, turn off when released. (momentary turbo) - (in Simple UI, this uses the ceiling level instead of turbo) - -While the light is on, a few actions are available: - - - 1C: Turn off. - - 2C: Go to or from the turbo level. - (or if it has regulated down, "bump" back up to turbo) - (turbo level / behavior is configurable) - - 1H: Change brightness (up). If the button was released less than a - second ago, or if it's already at the ceiling, it goes down instead. - - 2H: Change brightness (down). - - - 3C: Switch to the other ramp style. (smooth / stepped) - (or activate the next channel mode, when more than one is enabled) - (then use 6C instead, for smooth / stepped toggle) - - 6C: Switch to the other ramp style. (when 3C is mapped to next channel) - - - 3H: Momentary turbo (when current channel has no tint to ramp). - - 3H: Tint ramping (only when current channel has adjustable tint). - - 4H: Momentary turbo, when 3H is mapped to tint. - - - 4C: Go to lockout mode. - - - 5C: Go to momentary mode. - - 5H: Start a sunset timer. Details are below in the Sunset Timer section. - - - 7H: Ramp config menu. - - Item 1: Floor level. - - Item 2: Ceiling level. - - Item 3: - Stepped ramp: Number of steps. Can be 1 to 150. - Smooth ramp: Ramp speed. - 1 = Full speed, ~2.5s from end to end. - 2 = Half speed, ~5s from end to end. - 3 = Third speed, ~7.5s. - 4 = Quarter speed, ~10s. - - - 10C: Activate manual memory and save the current brightness. - - 10H: Ramp extras config menu. - - Item 1: Disable manual memory and go back to automatic memory. - (doesn't matter what value the user enters at the prompt) - - Item 2: Configure the manual memory timer. - Sets the timer to N minutes, where N is the number of - clicks. A value of 0 (no clicks) turns the timer off. - - Item 3: Configure whether to ramp up after "Off -> 1H". - 0: Ramp up after moon. - 1: Don't ramp up, just stay at the floor level. - - Item 4: Configure Advanced UI's turbo style: - 0: No turbo, only ceiling. - 1: Anduril 1 style. Ramp -> 2C goes to full power. - 2: Anduril 2 style. Ramp -> 2C goes to ceiling, - or goes to full power if user ramped up to ceiling first. - This value also affects momentary turbo in Ramp and Off modes. - - Item 5: Configure "smooth steps". - 0: Disable smooth steps. - 1: Enable smooth steps. - -Memory determines which brightness level the light goes to with 1 click -from off. There are three types of brightness memory to choose from: - - - Automatic: Always uses the last-ramped brightness. - (does not memorize levels accessed by a shortcut, - like turbo, 2C for ceiling, or 1H-from-off for floor) - - - Manual: Always uses the user's saved brightness. - - - Hybrid: Uses the automatic memory brightness if the light was only - off for a short time, or resets to the manual memory level if it was - off for a longer time. - The timer for this is configurable from 0 to ~140 minutes. - -Another way to think of it is: There are three styles of memory for the -last-ramped brightness level... - - - Always remember (automatic) - - Remember for N minutes (hybrid) - - Never remember (manual) - -To choose a memory style, set the configuration accordingly: - - mem type manual mem manual mem timer - -------- ---------- ---------------- - automatic off any - manual on zero - hybrid on non-zero - -If "smooth steps" is enabled, the stepped ramp uses a smooth animation -between steps, and turning the light on/off has the edges smoothed off -too. With "smooth steps" turned off, these brightness changes are -immediate. - - -Sunset Timer ------------- - -In the ramp mode or candle mode, it's possible to make the light turn -itself off after a while. - -To activate the timer, go to the brightness you want and then use a 5H -action. Keep holding the button, and the light should blink once per -second. Each blink adds 5 minutes to the timer. - -In ramp mode, it slowly dims until it's at the lowest level, then shuts -off. In candle mode, it stays at the same brightness until the final -minute, at which point it dims and dies out. - -The user can change the brightness while the timer is active. If this -happens during the final few minutes, it also "bumps" the timer up to a -minimum of 3 minutes. So if it's getting really dim and you need a -little more time, you could do a 5H to add 5 minutes, or simply ramp up -to the desired brightness. - - -Other Modes ------------ - -Anduril has several other modes too. To access these, press the button -more than 2 times when the light is off: - - - 3C: Blinky / utility modes, starting with battery check. - - 3H: Strobe modes, starting with the most recently used strobe. - - 4C: Lockout mode. - - 5C: Momentary mode. - - 6C: Tactical mode. - - 7C / 7H: Aux LED configuration. - - 9H: Misc Config menu. (only on some lights) - - 10H: Simple UI configuration menu. - - 13H: Factory reset (on some lights). - - 15C or more: Version check. - - -Lockout Mode ------------- - -Click 4 times from Off to enter Lockout mode. Or 4 times from Ramp. -This makes the light safe to carry in a pocket or a bag or anywhere else -the button might be pressed by accident. - -To exit lockout mode, click 4 times. The light should blink briefly and -then turn on at the memorized level. Or hold the final press to turn on -at the floor level instead: - - - 3C: Unlock and go to "Off" mode - - - 4C: Go to ramp mode (memorized level). - (uses manual mem level if there is one) - - - 4H: Go to ramp mode (floor level). - - - 5C: Go to ramp mode (ceiling level). - -Lockout mode also doubles as a momentary moon mode, so the user can do -quick tasks without having to unlock the light. The brightness in -lockout mode has two levels: - - - 1H: Light up at the lowest floor level. - - - 2H: Light up at the highest floor level. - (or the manual mem level, if there is one) - - - 3H: Next channel mode (if more than one is enabled). - -It is also possible to make the light lock itself automatically after -being turned off. To enable this, go to lockout mode and use a 10H -action to activate the auto-lock config menu. Release the button after -the first blink. Then at the prompt, click N times to set the auto-lock -timeout to N minutes. - - - 10H: Auto-lock config menu. Click N times to set timeout to N minutes. - A value of zero disables the auto-lock feature. - So, to turn off auto-lock, don't click at all. - -And on lights which have aux LEDs, there may be additional functions: - - - 7C / 7H: Change Lockout Mode's aux LED pattern. More details on - this below, in a separate section. - - -Blinky / Utility Modes ----------------------- - -Click 3 times from Off to access Anduril's blinky / utility modes. This -always starts at battery check and the user can proceed to other blinky -modes if Advanced UI is enabled. The sequence is: - - - Battery check. - - Temperature check (if light has a temperature sensor). - - Beacon mode. - - SOS mode (if enabled). - -In all of these modes, some basic actions are available: - - - Click: Turn off. - - 2 clicks: Next blinky mode. - -Additionally, in battery check and temperature check modes: - - - 7H: Go to the voltage config menu or thermal config menu. - -In more detail, this is what each blinky / utility mode does: - - Battery check: - - Blinks out the battery voltage per cell. Full is 4.2V, empty is - about 3.0V. The light blinks the whole-number digit first, pauses, - then blinks out the "tenths" digit. Then a longer pause, and it - repeats. - So for 4.2V, it would be "blink-blink-blink-blink .. blink-blink". - - A "zero" digit is represented by a very quick blink. - - On lights with more than one set of LEDs, pressing 3C during batt - check mode can select which set of LEDs (which channel mode) it uses - to blink out numbers. - - The voltage config menu has these settings: - - 1. Voltage correction factor. This adjusts the battery - measurement sensor, allowing the user to add or subtract up to - 0.30V in 0.05V steps. Click N times to enter a value: - - 1C: -0.30V - 2C: -0.25V - 3C: -0.20V - 4C: -0.15V - 5C: -0.10V - 6C: -0.05V - 7C: default, 0V - 8C: +0.05V - 9C: +0.10V - 10C: +0.15V - 11C: +0.20V - 12C: +0.25V - 13C: +0.30V - - 2. Post-off voltage display timeout. (only on lights with RGB aux) - This setting determines how many seconds the RGB aux LEDs - display the voltage color after the torch goes to sleep. Click - once per desired second, or zero times to turn this function - off. - - Temperature check: - - Blinks out the current temperature in degrees C. This number should - be pretty close to what a real thermometer says. If not, it would - be a good idea to enter the thermal config menu and calibrate the - sensor. Or let the light settle to room temperature, then use - factory reset to auto-calibrate the sensor. - - The thermal config menu has two settings: - - - Current temperature. Click once per degree C to calibrate the - sensor. For example, if the ambient temperature is 21 C, then - click 21 times. - - - Temperature limit. This sets the maximum temperature the light - can reach before it will start doing thermal regulation to keep - itself from overheating. Click once per degree C above 30. For - example, to set the limit to 50 C, click 20 times. The default is - 45 C, and the highest value it will allow is 70 C. - - Beacon mode: - - Blinks at a slow speed. The light stays on for 100ms, and then - stays off until the next blink. The brightness and the number of - seconds between pulses are configurable: - - - Brightness is the user's memorized ramp level, so set this in - ramping mode before activating beacon mode. Follows the same - memory rules as ramping -- automatic, manual, or hybrid. - - - Speed is configured by holding the button. The light should - blink once per second while holding the button. Release it - after the desired amount of time has passed, to set a new beacon - speed. - For example, to do a 10-second alpine beacon, hold the button - for 10 seconds. - - SOS mode: - - Blinks out a distress signal. Three short, three long, three short. - Repeats until light is turned off or until battery is low. - - The memorized ramp level determines the brightness of SOS Mode. - - -Strobe / Mood Modes -------------------- - -Anduril includes a few extra modes for a variety of purposes: - - - Candle mode - - Bike flasher - - Party strobe - - Tactical strobe - - Lightning storm mode - -Click 3 times from Off to access these, but hold the third click for a -moment. Click, click, hold. The last-used strobe mode is remembered, -so it will return to whichever one you used last. - -In all of these modes, a few actions are available: - - - 1C: Turn off. - - 2C: Next strobe / mood mode. - - 1H: Increase brightness, or strobe faster. (except lightning) - - 2H: Decrease brightness, or strobe slower. (except lightning) - - 4C: Previous strobe / mood mode. - - 5C: Go to momentary mode, for a momentary strobe. - (this is useful for light painting) - -Additionally, candle mode has one more action: - - - 5H: Activate Sunset Timer, and/or add 5 minutes to the timer. - -In more detail, here is what each mode does: - - - Candle mode - - Brightness changes randomly in a pattern resembling a candle flame. - If a timer is set, it will run until the timer expires, then get - dimmer for one minute, then sputter and turn itself off. Without a - timer, candle mode runs until the user turns it off. Brightness is - configurable. - - - Bike flasher - - Runs at a medium level, but stutters to a brighter level once per - second. Designed to be more visible than a normal ramping mode, but - otherwise works mostly the same. Brightness is configurable. - - - Party strobe - - Motion-freezing strobe light. Can be used to freeze spinning fans - and falling water. Speed is configurable. - - - Tactical strobe - - Disorienting strobe light. Can be used to irritate people. Speed - is configurable, and the duty cycle is always 33%. - - Be careful about heat in this mode, if using it for a long time. - - - Police strobe (on some lights) - - 2-color police style strobe. Only works on lights with 2 or more - colors. - - - Lightning storm mode - - Flashes at random brightness and random speed to simulate lightning - strikes during a busy lightning storm. Do not look directly at the - flashlight when this mode is running, because it may suddenly go to - full power without warning. - - -Momentary Mode --------------- - -Click 5 times from Off to enter Momentary mode. Or 5 times from Ramp, -or 5 times from a strobe mode. - -This locks the flashlight into a single-mode interface where the LEDs -are only on when the button is held down. It is intended for Morse -code, light painting, and other tasks where the light should be on only -for a short time and probably in a pattern. - -Momentary mode does either a steady brightness level or a strobe, -depending on which was active before going to momentary mode. To select -which one, go to the mode you want to use, adjust the brightness and -speed and other settings, then click 5 times to enter momentary mode. - -In steady mode, brightness is the memorized ramp level, so adjust that -in Ramp Mode before entering momentary mode. - -In momentary strobe mode, the settings are copied from the last-used -strobe mode, such as party strobe, tactical strobe, or lightning. - -To exit this mode, physically disconnect power by unscrewing the tailcap -or battery tube. - - -Tactical Mode --------------- - -Click 6 times from Off to enter Tactical Mode, or 6 times in Tactical -Mode to exit and go back to "Off". - -Tactical Mode provides instant momentary access to high, low, and -strobe, but each of these is configurable. The inputs are: - - - 1H: High - - 2H: Low - - 3H: Strobe - -Each of these only lasts as long as you hold the button. - -Other commands in Tactical Mode are: - - - 6C: exit (go back to Off Mode) - - 7H: Tactical Mode config menu - - 1st blink: configure tactical slot 1 - - 2nd blink: configure tactical slot 2 - - 3rd blink: configure tactical slot 3 - -To change what is in a tactical slot, press 7H, then release the button -after the 1st, 2nd, or 3rd blink. Then enter a number. Each click adds -1, and each hold adds 10. The number can be: - - - 1 to 150: set the brightness level - - 0: last-used strobe mode - - 151+: go directly to a specific strobe mode - 151 = party strobe - 152 = tactical strobe - 153+ = other strobes, in the same order they're in in the "Off -> 3H" - strobe group - -This assumes the light has a ramp 150 levels long. Strobe modes start -at the ramp size plus 1, so it may be different if a light has a -different ramp size. - - -Configuration Menus -------------------- - -Every config menu has the same interface. It has one or more options -the user can configure, and it will go through them in order. For each -menu item, the light follows the same pattern: - - - Blink once, then go to a lower brightness. The user can keep - holding the button to skip this menu item, or release the button to - dive in and enter a new value. - - - If the user released the button: - - - Stutter or "buzz" quickly between two brightness levels for a few - seconds. This indicates that the user can click one or more times - to enter a number. It will keep buzzing until the user stops - clicking, so there is no need to hurry. - - The actions here are: - - click: add 1 - - hold: add 10 (only in versions 2021-09 or later) - - wait: exit - -After entering a number, or after skipping every menu item, it waits -until the button is released then exits the menu. It should return to -whatever mode the light was in before entering the config menu. - - -Ramp Config Menu ----------------- - -While the light is on in a ramping mode, click 7 times (but hold the -final click) to access the config menu for the current ramp. - -Or, to access the ramp config for Simple UI, make sure the Simple UI is -not active, then do a 10H action from Off. - -For smooth ramping mode, there are three menu options: - - 1. Floor. (default = 1/150) - 2. Ceiling. (default = 120/150) - 3. Ramp speed. (default = 1, fastest speed) - -For the stepped ramping mode, there are three menu options: - - 1. Floor. (default = 20/150) - 2. Ceiling. (default = 120/150) - 3. Number of steps. (default = 7) - -For the Simple UI mode, there are four menu options. The first three -are the same as stepped ramping mode. - - 1. Floor. (default = 20/150) - 2. Ceiling. (default = 120/150) - 3. Number of steps. (default = 5) - 4. Turbo style. (default = 0, no turbo) - -Default values are different for each model of flashlight. The numbers -above are only examples. - -To configure the floor level, click the button equal to the number of -ramp levels (out of 150) at which the floor should be. To set the -lowest possible level, click once. - -To configure the ceiling level, each click goes one level lower. So 1 -click sets the highest possible level, 2 clicks is the 2nd-highest, 3 -clicks is the 3rd-highest level, etc. To set the default of 120/150, -click 31 times. - -When configuring the number of steps, the value can be anything from 1 -to 150. A value of 1 is a special case. It places the step halfway -between the floor and ceiling levels. - - -Version Check Mode ------------------- - -This allows people to see which version of the firmware is installed on -their light. The format for this is (usually) 12 digits -- a model -number and a date. BBPP.YYYY-MM-DD - - - BB: Brand ID - - PP: Product ID - - YYYY: Year - - MM: Month - - DD: Day - -Versions before 2023-05-30 used YYYYMMDDBBPP format. -Anduril 1 used only YYYYMMDD format, or none at all. - -The date is when the firmware was compiled. If the vendor did not set -this value, it defaults to 1969-07-20, the date of first human contact -with the moon. However, it might not be a date at all; some vendors may -specify a completely different type of value here. - -The brand/product values are also known as the model number. These are -hard-coded in the source code for each light's build target, and can be -looked up in the "MODELS" file or by using the "make models" command. - - -Protection Features -------------------- - -Anduril includes low voltage protection (LVP) and thermal regulation. - -LVP makes the light step down to a lower level when the battery is low, -and if the light is already at the lowest level, it shuts itself off. -This activates at 2.8V. LVP adjustments happen suddenly, in large -steps. - -Thermal regulation attempts to keep the light from overheating, and -otherwise adjusts output to stay as close as possible to the -user-configured temperature limit. Thermal adjustments happen -gradually, in steps so small they are difficult for humans to perceive. - - -Aux LEDs / Button LEDs ----------------------- - -Some lights have aux LEDs or button LEDs. These can be configured to do -different things while the main emitters are off. There is one aux LED -mode for the regular "off" mode, and another aux LED mode for "lockout" -mode. This allows the user to see at a glance whether the light is -locked. - -Aux LED modes typically include: - - - Off - - Low - - High - - Blinking - -To configure the aux LEDs, go to the mode you want to configure and then -click the button 7 times. This should change the aux LEDs to the next -mode supported on this light. - - - 7C: Next aux LED mode. - -If the aux LEDs can change color, there are additional actions to change -the color. It is the same as above, but hold the button on the last -click and then let go when the desired color is reached. - - - 7H: Next aux LED color. - -On most lights, the colors follow this sequence: - - - Red - - Yellow (Red+Green) - - Green - - Cyan (Green+Blue) - - Blue - - Purple (Blue+Red) - - White (Red+Green+Blue) - - Disco (fast random colors) - - Rainbow (cycles through all colors in order) - - Voltage (uses color to display battery charge) - -In voltage mode, the colors follow the same sequence, in the same order -as a rainbow... with red indicating a low battery and purple indicating -a full battery. - -For lights with a button LED, the button LED typically stays on while -the main emitters are on. Its brightness level is set in a way which -mirrors the main LED -- off, low, or high. - -For lights with a RGB button LED, the button LED indicates battery -charge during use in the same manner as the aux LED voltage mode. - -For lights with front-facing aux LEDs, the aux LEDs typically stay off -when the main emitters are on, and when the light is otherwise awake. -The aux LEDs on most lights only turn on when the light is asleep. - -When a light has a single-color aux LED and no RGB, it fast-blinks the -aux LED in "off" modes when voltage is low. - - -Misc Config Menu ----------------- - -Some models may have an extra config menu for settings which don't fit -anywhere else. This menu is located at "Off -> 9H" in the advanced UI. - -These settings are, in order: - - - Tint ramp style: (on some lights) - - 0 : smooth ramp (blend channels in any proportion) - 1 : middle tint only - 2 : extreme tints only (only one channel active at a time) - 3+: stepped ramp with 3+ steps - - - Jump Start level: (on some lights) - - Some lights are prone to starting up slowly at low levels, so they - have an option to "jump start" the engine by pulsing a higher power - level for a few milliseconds when changing from off to a low level. - This setting specifies how bright that pulse should be. - - The value can be from 1 to 150, but is usually between 20 and 50. - -These settings are hardware-specific and may not be present on all -lights. The number of settings in the Misc Config Menu depends on the -hardware model and the firmware version. - - -Channel Modes (a.k.a. Tint Ramping or Multi Channel controls) -------------------------------------------------------------- - -Some lights have more than one set of LEDs which can be adjusted to -change the beam color, shape, or other properties. These lights have -features like tint ramping and channel modes. - -On these models, there are some global button mappings which work at all -times unless they're overridden by the mode the light is in: - - - 3C: Next channel mode - - 3H: Adjust current channel mode (ramp tint, for example) - - 9H: Channel mode config menu - -Details depend on the exact type of light used. For example, if a light -has LEDs in cool white, warm white, and red... that light might have a -few channel modes: - - - White blend (adjustable CCT / tint ramping) - - Red only - - Auto-tint - -On a light like this, the user could press 3C to rotate through these -different channel modes... white, then red, then auto, then back to -white. - -Additionally, in the "white blend" mode, the user could press 3H to -manually adjust the balance between warm white and cool white. - -Finally, if the user decides they don't want all of the modes, they can -turn some off. Press 9H (while on) to start the channel mode config -menu. To disable the auto-tint mode, for example, it is the 3rd mode... -so wait for the 3rd blink, then release the button. Then at the prompt, -enter a value of 0 (wait for the prompt to time out without clicking -anything). Afterward, the auto tint mode should no longer be in the -channel mode rotation. To turn the mode back on later, do the same -thing, but enter a value of 1 (click 1 time at the prompt). - -A light can have many different channel modes, so don't be shy about -turning off any modes you don't use. It makes all the others easier to -reach. - -If you turn off channel modes until only 1 remains, the "Ramp -> 3C" -action reverts to its single-channel behavior -- switching between a -smooth or stepped brightness ramp. Additionally, when a channel mode -has nothing to adjust with 3H, the 3H action also reverts to its -single-channel behavior -- momentary turbo. - -The Misc Config Menu (Off -> 9H) may also have a setting to choose a -tint ramp style. There are a few styles available, by entering -different numbers into that config menu: - - 0: smooth ramp - 1: middle tint only - 2: extreme tints only - 3+: stepped ramp with 3+ steps - -This setting only applies to modes with channel ramping (i.e. tint -ramping), and only when that mode uses the default 3H event handler. -Custom channel modes may work differently. - - - -UI Reference Table ------------------- - -This is a table of all button mappings in Anduril, in one place: - -Mode UI Button Action ----- -- ------ ------ -Off Any 1C On (ramp mode, memorized level) -Off Any 1H On (ramp mode, floor level) -Off Any 2C On (ramp mode, ceiling level) -Off Simple 2H On (momentary ceiling level) -Off Full 2H On (momentary turbo) -Off Any 3C Battcheck mode -Off Full 3H Strobe mode (whichever was used last) -Off Any 4C Lockout mode -Off Full 5C Momentary mode -Off Full 6C Tactical mode -Off Full 7C Aux LEDs: Next pattern -Off Full 7H Aux LEDs: Next color -Off Full 9H Misc Config menu (varies per light): - ?1: tint ramp style - ?2: jump start level -Off Full 10C Enable Simple UI -Off Simple 10H Disable Simple UI -Off Full 10H Simple UI ramp config menu: - 1: floor - 2: ceiling - 3: steps - 4: turbo style -Off Any 13H Factory reset (on some lights) -Off Any 15+C Version check - -Ramp Any 1C Off -Ramp Any 1H Ramp (up, with reversing) -Ramp Any 2H Ramp (down) -Ramp Any 2C Go to/from ceiling or turbo (configurable) -Ramp Full 3C Change ramp style (smooth / stepped) -Ramp Full 6C (same as above, but on multi-channel lights) -Ramp Full 3H Momentary turbo (when no tint ramping) -Ramp Full 4H Momentary turbo (on multi channel lights) -Ramp Any 4C Lockout mode -Ramp Full 5C Momentary mode -Ramp Full 5H Sunset timer on, and add 5 minutes -Ramp Full 7H Ramp config menu: (for current ramp) - 1: floor - 2: ceiling - 3: speed / steps -Ramp Full 10C Turn on manual memory and save current brightness -Ramp Full 10H Ramp Extras config menu: - 1: switch to automatic mem, not manual mem - 2: set manual mem timeout - 3: ramp after moon or not - 4: advanced UI turbo style - 5: smooth steps - -Multi-channel lights only: -Any Any 3C Next channel mode (i.e. next color mode) -Any Any 3H Tint ramp (if this mode can) -Any Full 9H Channel mode enable/disable menu: - N: click (or not) to enable (disable) mode N - -Lockout Any 1C/1H Momentary moon (lowest floor) -Lockout Any 2C/2H Momentary moon (highest floor, or manual mem level) -Lockout Any 3C Unlock (go to "Off" mode) -Lockout Any 3H Next channel mode (if more than one enabled) -Lockout Any 4C On (ramp mode, memorized level) -Lockout Any 4H On (ramp mode, floor level) -Lockout Any 5C On (ramp mode, ceiling level) -Lockout Full 7C Aux LEDs: Next pattern -Lockout Full 7H Aux LEDs: Next color -Lockout Full 10H Auto-lock config menu: - 1: set timeout in minutes (0 = no auto-lock) - -Strobe (any) Full 1C Off -Strobe (any) Full 2C Next strobe mode -Strobe (any) Full 3C Next channel mode (saved per strobe mode) -Strobe (any) Full 4C Prev strobe mode -Strobe (any) Full 5C Momentary mode (using current strobe) - -Party strobe Full 1H/2H Faster / slower -Tactical strobe Full 1H/2H Faster / slower -Police strobe - - None (brightness is Ramp Mode's last-used level) -Lightning Full 1H Interrupt current flash or start new one -Candle Full 1H/2H Brighter / dimmer -Candle Full 5H Sunset timer on, add 5 minutes -Biking Full 1H/2H Brighter / dimmer - -Batt check Any 1C Off -Batt check Full 2C Next blinky mode (Temp check, Beacon, SOS) -Batt check Full 3C Next channel mode (for number blinks only) -Batt check Full 7H Voltage config menu - 1: voltage correction factor - ... - 5: -0.10V - 6: -0.05V - 7: no correction - 8: +0.05V - 9: +0.10V - ... - 2: post-off voltage display seconds - -Temp check Full 1C Off -Temp check Full 2C Next blinky mode (Beacon, SOS, Batt check) -Temp check Full 7H Thermal config menu - 1: set current temperature - 2: set temperature limit - -Beacon Full 1C Off -Beacon Full 1H Configure beacon timing -Beacon Full 2C Next blinky mode (SOS, Batt check, Temp check) - -SOS Full 1C Off -SOS Full 2C Next blinky mode (Batt check, Temp check, Beacon) - -Momentary Full Any On (until button is released) -Momentary Full Disconnect power Exit Momentary mode - -Tactical Full 1H High (tactical slot 1) -Tactical Full 2H Low (tactical slot 2) -Tactical Full 3H Strobe (tactical slot 3) -Tactical Full 6C Exit (go back to Off Mode) -Tactical Full 7H Tactical Mode config menu: - 1: tactical slot 1 - 2: tactical slot 2 - 3: tactical slot 3 - -Config menus Full Hold Skip current item with no changes -Config menus Full Release Configure current item - (goes to Number Entry menu) - -Number entry Full Click Add 1 to value for current item -Number entry Full Hold Add 10 to value for current item - diff --git a/spaghetti-monster/anduril/anduril-ui.png b/spaghetti-monster/anduril/anduril-ui.png deleted file mode 100644 index 8f60cc8..0000000 Binary files a/spaghetti-monster/anduril/anduril-ui.png and /dev/null differ diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c deleted file mode 100644 index e46eeaf..0000000 --- a/spaghetti-monster/anduril/anduril.c +++ /dev/null @@ -1,396 +0,0 @@ -// Anduril: Narsil-inspired UI for SpaghettiMonster. -// (Anduril is Aragorn's sword, the blade Narsil reforged) -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -/* - * Usually a program would be structured like this... - * - Library headers - * - App headers - * - App code - * - * ... in each source file. - * ... and each library and part of the program would be linked together. - * - * But this doesn't follow that pattern, because it's using the - * -fwhole-program - * flag to reduce the compiled size. It lets us fit more features - * in a tiny MCU chip's ROM. - * - * So the structure is like this instead... - * - App-level configuration headers - * - Default config - * - Per build target config - * - Library-level configuration headers - * - Library code (FSM itself) - * - App headers - * - App code (all of it, inline) - * - * Don't do this in regular programs. It's weird and kind of gross. - * But in this case it gives us a bunch of much-needed space, so... woot. - * - * Also, there are a ton of compile-time options because it needs to build - * a bunch of different versions and each one needs to be trimmed as small - * as possible. These are mostly "USE" flags. - */ - -/********* User-configurable options *********/ -#include "config-default.h" - -/********* specific settings for known driver types *********/ -// Anduril config file name (set it here or define it at the gcc command line) -//#define CFG_H cfg-blf-q8.h - -#include "tk.h" -#include incfile(CFG_H) - - -/********* Include headers which need to be before FSM *********/ - -// enable FSM features needed by basic ramping functions -#include "ramp-mode-fsm.h" - -#ifdef USE_FACTORY_RESET -#include "factory-reset-fsm.h" -#endif - -#ifdef USE_BATTCHECK_MODE -#include "battcheck-mode-fsm.h" -#endif - -#ifdef USE_LOCKOUT_MODE -#include "lockout-mode-fsm.h" -#endif - -// enable FSM features needed by strobe modes -#include "strobe-modes-fsm.h" - -// figure out how many bytes of eeprom are needed, -// based on which UI features are enabled -// (include this one last) -#include "load-save-config-fsm.h" - - -/********* bring in FSM / SpaghettiMonster *********/ -#define USE_IDLE_MODE // reduce power use while awake and no tasks are pending - -#include "spaghetti-monster.h" - -/********* does this build target have special code to include? *********/ -#ifdef HWDEF_C_FILE -#include incfile(HWDEF_C_FILE) -#endif -#ifdef CFG_C_FILE -#include incfile(CFG_C_FILE) -#endif - - -/********* Include all the regular app headers *********/ - -#include "off-mode.h" -#include "ramp-mode.h" -#include "config-mode.h" -#include "aux-leds.h" -#include "misc.h" - -#ifdef USE_SUNSET_TIMER -#include "sunset-timer.h" -#endif - -#ifdef USE_VERSION_CHECK -#include "version-check-mode.h" -#endif - -#ifdef USE_BATTCHECK_MODE -#include "battcheck-mode.h" -#endif - -#ifdef USE_BEACON_MODE -#include "beacon-mode.h" -#endif - -#ifdef USE_THERMAL_REGULATION -#include "tempcheck-mode.h" -#endif - -#ifdef USE_LOCKOUT_MODE -#include "lockout-mode.h" -#endif - -#ifdef USE_MOMENTARY_MODE -#include "momentary-mode.h" -#endif - -#ifdef USE_TACTICAL_MODE -#include "tactical-mode.h" -#endif - -// allow the channel mode handler even when only 1 mode -// (so a tint ramp light could still use 3H even if there's no other mode) -#if defined(USE_CHANNEL_MODES) -#include "channel-modes.h" -#endif - -#ifdef USE_FACTORY_RESET -#include "factory-reset.h" -#endif - -// this one detects its own enable/disable settings -#include "strobe-modes.h" - -#ifdef USE_SOS_MODE -#include "sos-mode.h" -#endif - -#ifdef USE_SMOOTH_STEPS -#include "smooth-steps.h" -#endif - -// this should be last, so other headers have a chance to declare values -#include "load-save-config.h" - - -/********* Include all the app logic source files *********/ -// (is a bit weird to do things this way, -// but it saves a lot of space by letting us use the -fwhole-program flag) - -#include "off-mode.c" -#include "ramp-mode.c" -#include "load-save-config.c" -#include "config-mode.c" -#include "aux-leds.c" -#include "misc.c" - -#ifdef USE_SUNSET_TIMER -#include "sunset-timer.c" -#endif - -#ifdef USE_VERSION_CHECK -#include "version-check-mode.c" -#endif - -#ifdef USE_BATTCHECK_MODE -#include "battcheck-mode.c" -#endif - -#ifdef USE_BEACON_MODE -#include "beacon-mode.c" -#endif - -#ifdef USE_THERMAL_REGULATION -#include "tempcheck-mode.c" -#endif - -#ifdef USE_LOCKOUT_MODE -#include "lockout-mode.c" -#endif - -#ifdef USE_MOMENTARY_MODE -#include "momentary-mode.c" -#endif - -#ifdef USE_TACTICAL_MODE -#include "tactical-mode.c" -#endif - -#if defined(USE_CHANNEL_MODES) -#include "channel-modes.c" -#endif - -#ifdef USE_FACTORY_RESET -#include "factory-reset.c" -#endif - -#ifdef USE_STROBE_STATE -#include "strobe-modes.c" -#endif - -#ifdef USE_SOS_MODE -#include "sos-mode.c" -#endif - -#ifdef USE_SMOOTH_STEPS -#include "smooth-steps.c" -#endif - - -// runs one time at boot, when power is connected -void setup() { - - #ifndef START_AT_MEMORIZED_LEVEL - - // regular e-switch light, no hard clicky power button - - // blink at power-on to let user know power is connected - blink_once(); - - #ifdef USE_FACTORY_RESET - if (button_is_pressed()) - factory_reset(); - #endif - - load_config(); - - #if defined(USE_MANUAL_MEMORY) && defined(USE_MANUAL_MEMORY_TIMER) - // without this, initial boot-up brightness is wrong - // when manual mem is enabled with a non-zero timer - if (cfg.manual_memory) manual_memory_restore(); - #endif - - #if defined(USE_CHANNEL_MODES) - // add channel mode functions underneath every other state - push_state(channel_mode_state, 0); - #endif - - push_state(off_state, 1); - - #else // if START_AT_MEMORIZED_LEVEL - - // dual switch: e-switch + power clicky - // power clicky acts as a momentary mode - load_config(); - - #if defined(USE_CHANNEL_MODES) - // add channel mode functions underneath every other state - push_state(channel_mode_state, 0); - #endif - - if (button_is_pressed()) - // hold button to go to moon - push_state(steady_state, 1); - else - // otherwise use memory - push_state(steady_state, memorized_level); - - #endif // ifdef START_AT_MEMORIZED_LEVEL - -} - - -// runs repeatedly whenever light is "on" (not in standby) -void loop() { - - // "current_state" is volatile, so cache it to reduce code size - StatePtr state = current_state; - - #ifdef USE_AUX_RGB_LEDS_WHILE_ON - // display battery charge on RGB button during use - if (state == steady_state) - rgb_led_voltage_readout(actual_level > USE_AUX_RGB_LEDS_WHILE_ON); - #endif - - if (0) {} // placeholder - - #ifdef USE_VERSION_CHECK - else if (state == version_check_state) { - version_check_iter(); - } - #endif - - #ifdef USE_STROBE_STATE - else if ((state == strobe_state) - #ifdef USE_MOMENTARY_MODE - // also handle momentary strobes - || (( - (state == momentary_state) - #ifdef USE_TACTICAL_MODE - || (state == tactical_state) - #endif - ) - && (momentary_mode == 1) && (momentary_active)) - #endif - ) { - strobe_state_iter(); - } - #endif // #ifdef USE_STROBE_STATE - - #ifdef USE_BORING_STROBE_STATE - else if (state == boring_strobe_state) { - boring_strobe_state_iter(); - } - #endif - - #ifdef USE_BATTCHECK - else if (state == battcheck_state) { - battcheck(); - #ifdef USE_SIMPLE_UI - // in simple mode, turn off after one readout - // FIXME: can eat the next button press - // (state changes in loop() act weird) - if (cfg.simple_ui_active) set_state_deferred(off_state, 0); - else nice_delay_ms(1000); - #endif - } - #endif - - #ifdef USE_THERMAL_REGULATION - // TODO: blink out therm_ceil during thermal_config_state? - else if (state == tempcheck_state) { - blink_num(temperature); - nice_delay_ms(1000); - } - #endif - - #ifdef USE_BEACON_MODE - else if (state == beacon_state) { - beacon_mode_iter(); - } - #endif - - #if defined(USE_SOS_MODE) && defined(USE_SOS_MODE_IN_BLINKY_GROUP) - else if (state == sos_state) { - sos_mode_iter(); - } - #endif - - #ifdef USE_SMOOTH_STEPS - else if (cfg.smooth_steps_style && smooth_steps_in_progress) { - smooth_steps_iter(); - } - #endif - - #ifdef USE_IDLE_MODE - else { - // doze until next clock tick - idle_mode(); - } - #endif - -} - - -// instead of handling EV_low_voltage in each mode, -// it's handled globally here to make the code smaller and simpler -void low_voltage() { - - // "current_state" is volatile, so cache it to reduce code size - StatePtr state = current_state; - - // TODO: turn off aux LED(s) when power is really low - - if (0) {} // placeholder - - #ifdef USE_STROBE_STATE - // "step down" from strobe to something low - else if (state == strobe_state) { - set_state(steady_state, RAMP_SIZE/6); - } - #endif - - // in normal mode, step down or turn off - else if (state == steady_state) { - if (actual_level > 1) { - uint8_t lvl = (actual_level >> 1) + (actual_level >> 2); - set_level_and_therm_target(lvl); - } - else { - set_state(off_state, 0); - } - } - // all other modes, just turn off when voltage is low - else { - set_state(off_state, 0); - } - -} - diff --git a/spaghetti-monster/anduril/anduril.svg b/spaghetti-monster/anduril/anduril.svg deleted file mode 100644 index 2ecb530..0000000 --- a/spaghetti-monster/anduril/anduril.svg +++ /dev/null @@ -1,4921 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - Ramps: - Blinkies - Strobes - - - - - - - - - - - - - - - - - - BattCheck - - - - - TempCheck - - - - Beacon - - - - - Sunset - - - - ThermalCfg - - - - - BeaconCfg - - - - Ramp - Ceil - Floor - - - Turbo - - - - Mem - Regulated Hybrid -------------- Direct Drive - - - - - - - - - Ramp Cfg - - 4 Clicks - - - - 4 Clicks - - - - 1. Floor (click N times for level N)2. Ceiling (click N times for Nth highest)3. Number of steps (stepped ramp only) - 1. Current temperature (click N times for N deg C)2. Temperature limit (click N times for N + 30 C) - 1. Beacon speed (click N times for N seconds per flash) - - - Ramp - - Cfg - - - - - - Actions - 1 Click - Hold - 3 Clicks - Other Action - - - - 2 Clicks - Click, Hold - Andúril‎ UI - - - - - - OFF - - - Thermal Cfg - Beacon Cfg - - - - - 4 Clicks - 5 Clicks - Click, Click, Hold (3H) - - - - Brighter/ Faster - - - Dimmer/ Slower - - - Muggle - Momentary - - - 6 Clicks - - - - Smooth - - - - 4 Clicks - - - - - Stepped - - - - - - - - - - - - - - - - Bike - - Flasher - - - Party - - Strobe - - - Tactical - - Strobe - - - Lightning - - Storm - - - Candle - - - - - +30 min - - - - - - - - - - - - - - - - - - - - - OFF - - - - - - - - - - - - - OFF - - While On: - 1C - 1H - 3C - 2C - 2H - Brightness: Off, Low, High, Blinking - - 3C. Smooth /4C. Ramp Cfg5C. Manual Memory5H. Automatic Memory - Stepped ramp - - - - - - - - - - Color: R, Y, G, C, B, V, W, Rainbow, Volts - - - - - Lockout - - 7C - 7H - - Aux /ButtonLEDs - 3C - 3H - 3C - 3 Clicks - 3C - - - - - Lockout - - Momentary Moon / Low - - - - On / Strobes - - Factory Reset: Loosen tailcap, Hold button, Tighten tailcap, Hold 3s (or 13H from Off) - 3C - - - - - - - - diff --git a/spaghetti-monster/anduril/anduril.txt b/spaghetti-monster/anduril/anduril.txt deleted file mode 100644 index 0a3dc4c..0000000 --- a/spaghetti-monster/anduril/anduril.txt +++ /dev/null @@ -1,191 +0,0 @@ -From off: - - Ramp shortcuts: - * 1 click: memorized level - * Hold: lowest level then ramp up - * 2 clicks: highest ramp level - * Click, hold: highest level then ramp down - - Blinkies: - * 3 clicks: specials (battcheck, goodnight, beacon, tempcheck) - * Click, click, hold: strobes - (candle mode, bike flasher, party strobe, tactical strobe, - lightning storm mode) - (remembers which you last used) - - Other: - * 4 clicks: lock-out - * 5 clicks: momentary mode (disconnect power to exit) - * 6 clicks: muggle mode - * On hardware with an indicator LED... - * 7 clicks: Change aux LED mode used in "off" mode. - (the modes are usually off/low/high/blinking) - * 7 clicks (but hold the last click): - Change aux LED color used in "off" mode. - -In steady / ramping mode: - * 1 click: off - * Hold: ramp up - * Click, hold: ramp down - * 2 clicks: to/from turbo (actual turbo, not just highest ramp level) - * 3 clicks: toggle smooth vs discrete ramping - * 4 clicks: configure current ramp - * 5 clicks: activate manual memory and save current brightness - * 5 clicks (but hold the last click): go back to automatic memory - -Smooth ramp config mode: - * Setting 1: low end - (click N times to set ramp floor to level N) - * Setting 2: high end - (click N times to set ramp ceiling to level "151 - N") - -Discrete ramp config mode: - * Setting 1: low end - (click N times to set ramp floor to level N) - * Setting 2: high end - (click N times to set ramp ceiling to level "151 - N") - * Setting 3: number of levels in discrete ramp - (click N times to make discrete mode have N stair-steps) - (minimum 2, maximum 150) - -"Strobe" group modes: - - Candle mode: - * 1 click: off - * 2 clicks: next "strobe" group mode - * 3 clicks: add 30 minutes to the timer - (light will shut off when timer expires) - (default is no timer) - * Hold: brighter - * Click, hold: dimmer - - Bike flasher: - * 1 click: off - * 2 clicks: next "strobe" group mode - * Hold: brighter - * Click, hold: dimmer - - Party / Tactical strobe modes: - * 1 click: off - * Hold: change speed (faster) - * Click, hold: change speed (slower) - * 2 clicks: next "strobe" group mode - (TODO: random/police strobe?) - - Lightning storm mode: - * 1 click: off - * 2 clicks: next "strobe" group mode - -"Blinky" group modes: - - Battcheck mode: - * 1 click: off - * 2 clicks: goodnight mode - - Goodnight mode: - * 1 click: off - * 2 clicks: beacon mode - - Beacon mode: - * 1 click: off - * 2 clicks: tempcheck mode - * 4 clicks: configure time between pulses - - Beacon config mode: - * At buzz, click N times to set beacon frequency to N seconds. - - Tempcheck mode: - * 1 click: off - * 2 clicks: battcheck mode - * 4 clicks: thermal config mode - - Thermal config mode: - * Setting 1: calibrate sensor: - At buzz, click N times for N degrees C. For example, if the light - is current at "room temperature" of 22 C, click 22 times. Is - intended to only be done once upon initial setup, or not at all. - * Setting 2: temperature limit: - At buzz, click N times to set thermal limit to roughly 30 C + N. - -Lockout mode: - * Hold: momentary moon (current ramp floor) - * Click, Hold: momentary moon (other ramp floor) - * 4 clicks: exit lockout (return to regular "off" mode) - * On hardware with an indicator LED... - * 3 clicks: Change aux LED brightness used in lockout mode. - (the modes are usually off/low/high/blinking) - * 3 clicks (but hold the last click): - Change aux LED color used in lockout mode. - -Momentary mode: - * Press button: Light on (at memorized mode/level). - (uses either a steady mode or a strobe-group mode) - * Release button: Light off. - * To exit, disconnect power. (loosen/tighten the tailcap) - -Muggle mode: - * 1 click: On / off. - * Hold: Ramp up / down. - * 6 clicks: Exit muggle mode. - -Indicator LED / aux LED support: - - The indicator LED (button LED), if there is one, tracks the brightness - of the main LED(s) during use. It'll be off, low, or high depending - on whether the main emitter is off, using only the first power - channel, or using a higher level. - - If front-facing aux LEDs are present, they stay off while the main - light is on. They only turn on when the main emitters are off. - - The indicator LED can stay on when the light is in the "off" state or - "lockout" state. These modes can have different settings, to make it - easier to see which one the light is in. To configure it: - - 1. Go to the standby mode you want to configure. This is either - "off" or "lockout". - 2. In "off" mode, click 7 times to switch to the next aux LED mode. - Or in "lockout" mode, click 3 times. - 3. Repeat step 2 until the desired mode is reached. - - The available aux LED / indicator LED modes vary depending on the type - of light, but they typically have the following modes: - - - Off - - Low - - High - - Blinking - - If the aux LEDs can change color, the user can configure the color - using exactly the same method... except hold the last click until the - desired color is reached. - - -TODO: - - change 6C to 6H for exiting muggle mode? - - move muggle mode from 6C to ... 8C or something? - - add 4H from lockout to turn light on and start ramping up? - - move config modes to 5C instead of 4C, and move manual memory to 4C? - - remove beacon config mode, and use a hold to set timing instead? - - rewrite muggle mode to split it into on and off states - * save settings in eeprom - * decide on "hold until hot" or "click N times" for thermal config mode - * test thermal regulation on an actual light - * improve thermal regulation - - a way to blink out the firmware version? - * indicator LED support - * a way to configure indicator LED behavior - * Go to lockout mode, then click three times. Hold the third click - to change regular "off" brightness, or make it a fast click to - change lockout mode brightness. - * add goodnight mode? - * add lightning mode? - * muggle mode: smooth ramp - * refactor to make config modes smaller - * move all config menus to four clicks - * candle mode timer, with three clicks to add 30 minutes - * diagram updates for 3-click special actions - * add a toggle for memory (manual vs automatic memory) - - candle mode: smoother adjustments? - - make sunset mode timer and brightness configurable? - - make beacon mode actually sleep between pulses diff --git a/spaghetti-monster/anduril/aux-leds.c b/spaghetti-monster/anduril/aux-leds.c deleted file mode 100644 index af59aa6..0000000 --- a/spaghetti-monster/anduril/aux-leds.c +++ /dev/null @@ -1,210 +0,0 @@ -// aux-leds.c: Aux LED functions for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#include "aux-leds.h" - - -#if defined(USE_INDICATOR_LED) -void indicator_led_update(uint8_t mode, uint8_t tick) { - //uint8_t volts = voltage; // save a few bytes by caching volatile value - // turn off when battery is too low - #ifdef DUAL_VOLTAGE_FLOOR - if (((voltage < VOLTAGE_LOW) && (voltage > DUAL_VOLTAGE_FLOOR)) - || (voltage < DUAL_VOLTAGE_LOW_LOW)) { - #else - if (voltage < VOLTAGE_LOW) { - #endif - indicator_led(0); - } - //#ifdef USE_INDICATOR_LOW_BAT_WARNING - #ifndef DUAL_VOLTAGE_FLOOR // this isn't set up for dual-voltage lights like the Sofirn SP10 Pro - // fast blink a warning when battery is low but not critical - else if (voltage < VOLTAGE_RED) { - indicator_led(mode & (((tick & 0b0010)>>1) - 3)); - } - #endif - //#endif - // normal steady output, 0/1/2 = off / low / high - else if ((mode & 0b00001111) < 3) { - indicator_led(mode); - } - // beacon-like blinky mode - else { - #ifdef USE_OLD_BLINKING_INDICATOR - - // basic blink, 1/8th duty cycle - if (! (tick & 7)) { - indicator_led(2); - } - else { - indicator_led(0); - } - - #else - - // fancy blink, set off/low/high levels here: - static const uint8_t seq[] = {0, 1, 2, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0}; - indicator_led(seq[tick & 15]); - - #endif // ifdef USE_OLD_BLINKING_INDICATOR - } -} -#endif - -#if defined(USE_AUX_RGB_LEDS) && defined(TICK_DURING_STANDBY) -uint8_t voltage_to_rgb() { - static const uint8_t levels[] = { - // voltage, color - 0, 0, // black - #ifdef DUAL_VOLTAGE_FLOOR - // AA / NiMH voltages - 9, 1, // R - 10, 2, // R+G - 11, 3, // G - 12, 4, // G+B - 13, 5, // B - 14, 6, // R + B - 15, 7, // R+G+B - 20, 0, // black - #endif - // li-ion voltages - 29, 1, // R - 33, 2, // R+G - 35, 3, // G - 37, 4, // G+B - 39, 5, // B - 41, 6, // R + B - 44, 7, // R+G+B // skip; looks too similar to G+B - 255, 7, // R+G+B - }; - uint8_t volts = voltage; - //if (volts < VOLTAGE_LOW) return 0; - - uint8_t i; - for (i = 0; volts >= levels[i]; i += 2) {} - uint8_t color_num = levels[(i - 2) + 1]; - return pgm_read_byte(rgb_led_colors + color_num); -} - -// do fancy stuff with the RGB aux LEDs -// mode: 0bPPPPCCCC where PPPP is the pattern and CCCC is the color -// arg: time slice number -void rgb_led_update(uint8_t mode, uint16_t arg) { - static uint8_t rainbow = 0; // track state of rainbow mode - static uint8_t frame = 0; // track state of animation mode - - // turn off aux LEDs when battery is empty - // (but if voltage==0, that means we just booted and don't know yet) - uint8_t volts = voltage; // save a few bytes by caching volatile value - #ifdef DUAL_VOLTAGE_FLOOR - if ((volts) && (((voltage < VOLTAGE_LOW) && (voltage > DUAL_VOLTAGE_FLOOR)) || (voltage < DUAL_VOLTAGE_LOW_LOW))) { - #else - if ((volts) && (volts < VOLTAGE_LOW)) { - #endif - rgb_led_set(0); - #ifdef USE_BUTTON_LED - button_led_set(0); - #endif - return; - } - - uint8_t pattern = (mode>>4); // off, low, high, blinking, ... more? - uint8_t color = mode & 0x0f; - - // always preview in high mode - if (setting_rgb_mode_now) { pattern = 2; } - - #ifdef USE_POST_OFF_VOLTAGE - // use voltage high mode for a few seconds after initial poweroff - // (but not after changing aux LED settings and other similar actions) - else if ((arg < (cfg.post_off_voltage * SLEEP_TICKS_PER_SECOND)) - && (ticks_since_on < (cfg.post_off_voltage * SLEEP_TICKS_PER_SECOND)) - && (ticks_since_on > 0) // don't blink red on 1st frame - ) { - // use high mode if regular aux level is high or prev level was high - pattern = 1 + ((2 == pattern) | (prev_level >= POST_OFF_VOLTAGE_BRIGHTNESS)); - // voltage mode - color = RGB_LED_NUM_COLORS - 1; - } - #endif - - const uint8_t *colors = rgb_led_colors + 1; - uint8_t actual_color = 0; - if (color < 7) { // normal color - actual_color = pgm_read_byte(colors + color); - } - else if (color == 7) { // disco - rainbow = (rainbow + 1 + pseudo_rand() % 5) % 6; - actual_color = pgm_read_byte(colors + rainbow); - } - else if (color == 8) { // rainbow - uint8_t speed = 0x03; // awake speed - if (go_to_standby) speed = RGB_RAINBOW_SPEED; // asleep speed - if (0 == (arg & speed)) { - rainbow = (rainbow + 1) % 6; - } - actual_color = pgm_read_byte(colors + rainbow); - } - else { // voltage - // show actual voltage while asleep... - if (go_to_standby) { - actual_color = voltage_to_rgb(); - // choose a color based on battery voltage - //if (volts >= 38) actual_color = pgm_read_byte(colors + 4); - //else if (volts >= 33) actual_color = pgm_read_byte(colors + 2); - //else actual_color = pgm_read_byte(colors + 0); - } - // ... but during preview, cycle colors quickly - else { - actual_color = pgm_read_byte(colors + (((arg>>1) % 3) << 1)); - } - } - - // pick a brightness from the animation sequence - if (pattern == 3) { - // uses an odd length to avoid lining up with rainbow loop - static const uint8_t animation[] = {2, 1, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 1}; - frame = (frame + 1) % sizeof(animation); - pattern = animation[frame]; - } - uint8_t result; - #ifdef USE_BUTTON_LED - uint8_t button_led_result; - #endif - switch (pattern) { - case 0: // off - result = 0; - #ifdef USE_BUTTON_LED - button_led_result = 0; - #endif - break; - case 1: // low - result = actual_color; - #ifdef USE_BUTTON_LED - button_led_result = 1; - #endif - break; - default: // high - result = (actual_color << 1); - #ifdef USE_BUTTON_LED - button_led_result = 2; - #endif - break; - } - rgb_led_set(result); - #ifdef USE_BUTTON_LED - button_led_set(button_led_result); - #endif -} - -void rgb_led_voltage_readout(uint8_t bright) { - uint8_t color = voltage_to_rgb(); - if (bright) color = color << 1; - rgb_led_set(color); -} -#endif - diff --git a/spaghetti-monster/anduril/aux-leds.h b/spaghetti-monster/anduril/aux-leds.h deleted file mode 100644 index fa97e6b..0000000 --- a/spaghetti-monster/anduril/aux-leds.h +++ /dev/null @@ -1,65 +0,0 @@ -// aux-leds.h: Aux LED functions for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY) -void indicator_led_update(uint8_t mode, uint8_t tick); -#endif -#if defined(USE_AUX_RGB_LEDS) && defined(TICK_DURING_STANDBY) -uint8_t setting_rgb_mode_now = 0; -void rgb_led_update(uint8_t mode, uint16_t arg); -void rgb_led_voltage_readout(uint8_t bright); -/* - * 0: R - * 1: RG - * 2: G - * 3: GB - * 4: B - * 5: R B - * 6: RGB - * 7: rainbow - * 8: voltage - */ -const PROGMEM uint8_t rgb_led_colors[] = { - 0b00000000, // 0: black - 0b00000001, // 1: red - 0b00000101, // 2: yellow - 0b00000100, // 3: green - 0b00010100, // 4: cyan - 0b00010000, // 5: blue - 0b00010001, // 6: purple - 0b00010101, // 7: white -}; -// intentionally 1 higher than total modes, to make "voltage" easier to reach -// (at Hank's request) -#define RGB_LED_NUM_COLORS 11 -#define RGB_LED_NUM_PATTERNS 4 -#ifndef RGB_LED_OFF_DEFAULT -#define RGB_LED_OFF_DEFAULT 0x19 // low, voltage -//#define RGB_LED_OFF_DEFAULT 0x18 // low, rainbow -#endif -#ifndef RGB_LED_LOCKOUT_DEFAULT -#define RGB_LED_LOCKOUT_DEFAULT 0x39 // blinking, voltage -//#define RGB_LED_LOCKOUT_DEFAULT 0x37 // blinking, disco -#endif -#ifndef RGB_RAINBOW_SPEED -#define RGB_RAINBOW_SPEED 0x0f // change color every 16 frames -#endif -#endif - -//#define USE_OLD_BLINKING_INDICATOR -//#define USE_FANCIER_BLINKING_INDICATOR -#ifdef USE_INDICATOR_LED - // bits 2-3 control lockout mode - // bits 0-1 control "off" mode - // modes are: 0=off, 1=low, 2=high, 3=blinking (if TICK_DURING_STANDBY enabled) - #ifndef INDICATOR_LED_DEFAULT_MODE - #ifdef USE_INDICATOR_LED_WHILE_RAMPING - #define INDICATOR_LED_DEFAULT_MODE ((2<<2) + 1) - #else - #define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) - #endif - #endif -#endif - diff --git a/spaghetti-monster/anduril/battcheck-mode-fsm.h b/spaghetti-monster/anduril/battcheck-mode-fsm.h deleted file mode 100644 index 5f8e8ec..0000000 --- a/spaghetti-monster/anduril/battcheck-mode-fsm.h +++ /dev/null @@ -1,18 +0,0 @@ -// battcheck-mode-fsm.h: FSM config for battery check mode in Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define USE_BATTCHECK - -#ifdef USE_AUX_RGB_LEDS - // show voltage colors for a few seconds after going to standby - #define USE_POST_OFF_VOLTAGE - #ifndef DEFAULT_POST_OFF_VOLTAGE_SECONDS - #define DEFAULT_POST_OFF_VOLTAGE_SECONDS 4 - #endif - #ifndef POST_OFF_VOLTAGE_BRIGHTNESS - // level at which to switch from low to high aux brightness - #define POST_OFF_VOLTAGE_BRIGHTNESS (RAMP_SIZE/10) - #endif -#endif diff --git a/spaghetti-monster/anduril/battcheck-mode.c b/spaghetti-monster/anduril/battcheck-mode.c deleted file mode 100644 index 462540e..0000000 --- a/spaghetti-monster/anduril/battcheck-mode.c +++ /dev/null @@ -1,82 +0,0 @@ -// battcheck-mode.c: Battery check mode for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#include "battcheck-mode.h" - -uint8_t battcheck_state(Event event, uint16_t arg) { - ////////// Every action below here is blocked in the simple UI ////////// - #ifdef USE_SIMPLE_UI - if (cfg.simple_ui_active) { - return EVENT_NOT_HANDLED; - } - #endif - - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - - // 2 clicks: next blinky mode - else if (event == EV_2clicks) { - #if defined(USE_THERMAL_REGULATION) - set_state(tempcheck_state, 0); - #elif defined(USE_BEACON_MODE) - set_state(beacon_state, 0); - #elif defined(USE_SOS_MODE) && defined(USE_SOS_MODE_IN_BLINKY_GROUP) - set_state(sos_state, 0); - #endif - return EVENT_HANDLED; - } - - #ifdef DEFAULT_BLINK_CHANNEL - // 3 clicks: next channel mode (specific to number blinky modes) - else if (event == EV_3clicks) { - cfg.blink_channel = (cfg.blink_channel + 1) % NUM_CHANNEL_MODES; - save_config(); - return EVENT_HANDLED; - } - #endif // ifdef DEFAULT_BLINK_CHANNEL - - #ifdef USE_VOLTAGE_CORRECTION - // 7H: voltage config mode - else if (event == EV_click7_hold) { - push_state(voltage_config_state, 0); - return EVENT_HANDLED; - } - #endif - - return EVENT_NOT_HANDLED; -} - -#ifdef USE_VOLTAGE_CORRECTION -// the user can adjust the battery measurements... on a scale of 1 to 13 -// 1 = subtract 0.30V -// 2 = subtract 0.25V -// ... -// 7 = no effect (add 0V) -// 8 = add 0.05V -// ... -// 13 = add 0.30V -void voltage_config_save(uint8_t step, uint8_t value) { - #ifdef USE_POST_OFF_VOLTAGE - if (2 == step) cfg.post_off_voltage = value; - else - #endif - if (value) cfg.voltage_correction = value; -} - -uint8_t voltage_config_state(Event event, uint16_t arg) { - #ifdef USE_POST_OFF_VOLTAGE - #define VOLTAGE_CONFIG_STEPS 2 - #else - #define VOLTAGE_CONFIG_STEPS 1 - #endif - return config_state_base(event, arg, - VOLTAGE_CONFIG_STEPS, - voltage_config_save); -} -#endif // #ifdef USE_VOLTAGE_CORRECTION - diff --git a/spaghetti-monster/anduril/battcheck-mode.h b/spaghetti-monster/anduril/battcheck-mode.h deleted file mode 100644 index b505b68..0000000 --- a/spaghetti-monster/anduril/battcheck-mode.h +++ /dev/null @@ -1,12 +0,0 @@ -// battcheck-mode.h: Battery check mode for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -uint8_t battcheck_state(Event event, uint16_t arg); - -#ifdef USE_VOLTAGE_CORRECTION -void voltage_config_save(uint8_t step, uint8_t value); -uint8_t voltage_config_state(Event event, uint16_t arg); -#endif - diff --git a/spaghetti-monster/anduril/beacon-mode.c b/spaghetti-monster/anduril/beacon-mode.c deleted file mode 100644 index 5aff508..0000000 --- a/spaghetti-monster/anduril/beacon-mode.c +++ /dev/null @@ -1,53 +0,0 @@ -// beacon-mode.c: Beacon mode for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#include "beacon-mode.h" - -inline void beacon_mode_iter() { - // one iteration of main loop() - if (! button_last_state) { - set_level(memorized_level); - nice_delay_ms(100); - set_level(0); - nice_delay_ms(((cfg.beacon_seconds) * 1000) - 100); - } -} - -uint8_t beacon_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // TODO: use sleep ticks to measure time between pulses, - // to save power - - // 2 clicks: next blinky mode - else if (event == EV_2clicks) { - #if defined(USE_SOS_MODE) && defined(USE_SOS_MODE_IN_BLINKY_GROUP) - set_state(sos_state, 0); - #elif defined(USE_BATTCHECK) - set_state(battcheck_state, 0); - #elif defined(USE_THERMAL_REGULATION) - set_state(tempcheck_state, 0); - #endif - return EVENT_HANDLED; - } - // hold: configure beacon timing - else if (event == EV_click1_hold) { - if (0 == (arg % TICKS_PER_SECOND)) { - blink_once(); - } - return EVENT_HANDLED; - } - // release hold: save new timing - else if (event == EV_click1_hold_release) { - cfg.beacon_seconds = 1 + (arg / TICKS_PER_SECOND); - save_config(); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - diff --git a/spaghetti-monster/anduril/beacon-mode.h b/spaghetti-monster/anduril/beacon-mode.h deleted file mode 100644 index df047ad..0000000 --- a/spaghetti-monster/anduril/beacon-mode.h +++ /dev/null @@ -1,9 +0,0 @@ -// beacon-mode.h: Beacon mode for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// beacon mode -uint8_t beacon_state(Event event, uint16_t arg); -inline void beacon_mode_iter(); - diff --git a/spaghetti-monster/anduril/build-all.sh b/spaghetti-monster/anduril/build-all.sh deleted file mode 100755 index b3fc5d3..0000000 --- a/spaghetti-monster/anduril/build-all.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/sh - -# Usage: build-all.sh [pattern] -# If pattern given, only build targets which match. - -if [ ! -z "$1" ]; then - SEARCH="$1" -fi - -UI=anduril - -date '+#define VERSION_NUMBER "%Y-%m-%d"' > version.h - -PASS=0 -FAIL=0 -PASSED='' -FAILED='' - -for TARGET in cfg-*.h ; do - - # maybe limit builds to a specific pattern - if [ ! -z "$SEARCH" ]; then - echo "$TARGET" | grep -i "$SEARCH" > /dev/null - if [ 0 != $? ]; then continue ; fi - fi - - # friendly name for this build - NAME=$(echo "$TARGET" | perl -ne '/cfg-(.*).h/ && print "$1\n";') - echo "===== $NAME =====" - - # figure out MCU type - ATTINY=$(grep 'ATTINY:' $TARGET | awk '{ print $3 }') - if [ -z "$ATTINY" ]; then ATTINY=85 ; fi - - # try to compile - echo ../../../bin/build.sh $ATTINY "$UI" "-DCFG_H=${TARGET}" - ../../../bin/build.sh $ATTINY "$UI" "-DCFG_H=${TARGET}" - - # track result, and rename compiled files - if [ 0 = $? ] ; then - mv -f "$UI".hex "$UI".$NAME.hex - PASS=$(($PASS + 1)) - PASSED="$PASSED $NAME" - else - echo "ERROR: build failed" - FAIL=$(($FAIL + 1)) - FAILED="$FAILED $NAME" - fi - -done - -# summary -echo "===== $PASS builds succeeded, $FAIL failed =====" -#echo "PASS: $PASSED" -if [ 0 != $FAIL ]; then - echo "FAIL:$FAILED" -fi diff --git a/spaghetti-monster/anduril/candle-mode.c b/spaghetti-monster/anduril/candle-mode.c deleted file mode 100644 index ab47c34..0000000 --- a/spaghetti-monster/anduril/candle-mode.c +++ /dev/null @@ -1,136 +0,0 @@ -// candle-mode.c: Candle mode for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#include "candle-mode.h" - -#ifdef USE_SUNSET_TIMER -#include "sunset-timer.h" -#endif - -uint8_t candle_mode_state(Event event, uint16_t arg) { - static int8_t ramp_direction = 1; - #define MAX_CANDLE_LEVEL (MAX_LEVEL-CANDLE_AMPLITUDE-15) - static uint8_t candle_wave1 = 0; - static uint8_t candle_wave2 = 0; - static uint8_t candle_wave3 = 0; - static uint8_t candle_wave2_speed = 0; - // these should add up to 100 - #define CANDLE_WAVE1_MAXDEPTH 30 - #define CANDLE_WAVE2_MAXDEPTH 45 - #define CANDLE_WAVE3_MAXDEPTH 25 - static const uint8_t candle_wave1_depth = CANDLE_WAVE1_MAXDEPTH * CANDLE_AMPLITUDE / 100; - static uint8_t candle_wave2_depth = CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100; - static uint8_t candle_wave3_depth = CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100; - static uint8_t candle_mode_brightness = 24; - - #ifdef USE_SUNSET_TIMER - // let the candle "burn out" and shut itself off - // if the user told it to - // cache this in case it changes when the timer is called - uint8_t sunset_active = sunset_timer; - // clock tick - sunset_timer_state(event, arg); - // if the timer just expired, shut off - if (sunset_active && (! sunset_timer)) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - #endif // ifdef USE_SUNSET_TIMER - - - if (event == EV_enter_state) { - ramp_direction = 1; - return EVENT_HANDLED; - } - #ifdef USE_SUNSET_TIMER - // 2 clicks: cancel timer - else if (event == EV_2clicks) { - // parent state just rotated through strobe/flasher modes, - // so cancel timer... in case any time was left over from earlier - sunset_timer = 0; - return EVENT_HANDLED; - } - #endif // ifdef USE_SUNSET_TIMER - // hold: change brightness (brighter) - else if (event == EV_click1_hold) { - // ramp away from extremes - if (! arg) { - if (candle_mode_brightness >= MAX_CANDLE_LEVEL) { ramp_direction = -1; } - else if (candle_mode_brightness <= 1) { ramp_direction = 1; } - } - // change brightness, but not too far - candle_mode_brightness += ramp_direction; - if (candle_mode_brightness < 1) candle_mode_brightness = 1; - else if (candle_mode_brightness > MAX_CANDLE_LEVEL) candle_mode_brightness = MAX_CANDLE_LEVEL; - return EVENT_HANDLED; - } - // reverse ramp direction on hold release - else if (event == EV_click1_hold_release) { - ramp_direction = -ramp_direction; - return EVENT_HANDLED; - } - // click, hold: change brightness (dimmer) - else if (event == EV_click2_hold) { - ramp_direction = 1; - if (candle_mode_brightness > 1) - candle_mode_brightness --; - return EVENT_HANDLED; - } - // clock tick: animate candle brightness - else if (event == EV_tick) { - // un-reverse after 1 second - if (arg == AUTO_REVERSE_TIME) ramp_direction = 1; - - // 3-oscillator synth for a relatively organic pattern - uint8_t add; - add = ((triangle_wave(candle_wave1) * candle_wave1_depth) >> 8) - + ((triangle_wave(candle_wave2) * candle_wave2_depth) >> 8) - + ((triangle_wave(candle_wave3) * candle_wave3_depth) >> 8); - uint16_t brightness = candle_mode_brightness + add; - - // self-timer dims the light during the final minute - #ifdef USE_SUNSET_TIMER - if (1 == sunset_timer) { - brightness = brightness - * ((TICKS_PER_MINUTE>>5) - (sunset_ticks>>5)) - / (TICKS_PER_MINUTE>>5); - } - #endif // ifdef USE_SUNSET_TIMER - - set_level(brightness); - - // wave1: slow random LFO - // TODO: make wave slower and more erratic? - if ((arg & 1) == 0) candle_wave1 += pseudo_rand() & 1; - // wave2: medium-speed erratic LFO - candle_wave2 += candle_wave2_speed; - // wave3: erratic fast wave - candle_wave3 += pseudo_rand() % 37; - // S&H on wave2 frequency to make it more erratic - if ((pseudo_rand() & 0b00111111) == 0) - candle_wave2_speed = pseudo_rand() % 13; - // downward sawtooth on wave2 depth to simulate stabilizing - if ((candle_wave2_depth > 0) && ((pseudo_rand() & 0b00111111) == 0)) - candle_wave2_depth --; - // random sawtooth retrigger - if (pseudo_rand() == 0) { - // random amplitude - //candle_wave2_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2)); - candle_wave2_depth = pseudo_rand() % (CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100); - //candle_wave3_depth = 5; - candle_wave2 = 0; - } - // downward sawtooth on wave3 depth to simulate stabilizing - if ((candle_wave3_depth > 2) && ((pseudo_rand() & 0b00011111) == 0)) - candle_wave3_depth --; - if ((pseudo_rand() & 0b01111111) == 0) - // random amplitude - //candle_wave3_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2)); - candle_wave3_depth = pseudo_rand() % (CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - diff --git a/spaghetti-monster/anduril/candle-mode.h b/spaghetti-monster/anduril/candle-mode.h deleted file mode 100644 index aab237d..0000000 --- a/spaghetti-monster/anduril/candle-mode.h +++ /dev/null @@ -1,13 +0,0 @@ -// candle-mode.h: Candle mode for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#ifndef CANDLE_AMPLITUDE -#define CANDLE_AMPLITUDE 25 -#endif - -uint8_t candle_mode_state(Event event, uint16_t arg); -// moved to fsm-misc.c because it's also used for tint ramping power correction -//uint8_t triangle_wave(uint8_t phase); - diff --git a/spaghetti-monster/anduril/cfg-blf-gt-mini.h b/spaghetti-monster/anduril/cfg-blf-gt-mini.h deleted file mode 100644 index 083f18e..0000000 --- a/spaghetti-monster/anduril/cfg-blf-gt-mini.h +++ /dev/null @@ -1,25 +0,0 @@ -// BLF/Lumintop GT Mini config options for Anduril -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#include "hwdef-blf-gt-mini.h" -// Same as an Emisar D1S, except it has a lighted button -#include "cfg-emisar-d1s.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0322" - -// the button lights up -#define USE_INDICATOR_LED -// the button is visible while main LEDs are on -#define USE_INDICATOR_LED_WHILE_RAMPING - -// too big, remove stuff to make room -#undef USE_TACTICAL_MODE -#undef USE_SOS_MODE -//#undef USE_RAMP_AFTER_MOON_CONFIG -//#undef USE_RAMP_SPEED_CONFIG -//#undef USE_VOLTAGE_CORRECTION -//#undef USE_2C_STYLE_CONFIG -//#undef USE_TACTICAL_STROBE_MODE - diff --git a/spaghetti-monster/anduril/cfg-blf-gt.h b/spaghetti-monster/anduril/cfg-blf-gt.h deleted file mode 100644 index 580318a..0000000 --- a/spaghetti-monster/anduril/cfg-blf-gt.h +++ /dev/null @@ -1,62 +0,0 @@ -// BLF GT config options for Anduril -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0321" -#include "hwdef-blf-gt.h" -// ATTINY: 85 - -// the button lights up -#define USE_INDICATOR_LED -// the button is visible while main LEDs are on -#define USE_INDICATOR_LED_WHILE_RAMPING - -#define RAMP_SIZE 150 - -// First 60 values: level_calc.py 1 60 7135 4 5.0 255 -// Remainder: all 255 (buck driver at 100% duty cycle) -#define PWM1_LEVELS 4,5,6,6,7,8,9,11,12,13,15,16,18,19,21,23,25,27,30,32,34,37,40,43,46,49,52,55,59,63,66,70,75,79,83,88,93,98,103,108,114,119,125,131,137,144,150,157,164,171,179,186,194,202,210,219,228,236,246,255, \ - 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -// First 60 values: all 25 (buck driver at 10% power) -// Remainder: values 61-150 of level_calc.py 1 150 7135 1 3 3000 -#define PWM2_LEVELS 25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25,25, \ - 26,27,28,29,30,31,32,33,35,36,37,38,40,41,42,44,45,47,48,50,51,53,54,56,58,59,61,63,65,67,69,70,72,74,76,79,81,83,85,87,89,92,94,96,99,101,104,106,109,112,114,117,120,123,125,128,131,134,137,140,143,147,150,153,156,160,163,167,170,174,177,181,184,188,192,196,200,204,208,212,216,220,224,228,233,237,241,246,250,255 -#define POWER_80PX 138 // 2.0 Amps out of maximum 2.5 Amps -#define MAX_1x7135 60 // where it switches from PWM to current control -#define DEFAULT_LEVEL 69 // nice -#define HALFSPEED_LEVEL 17 -#define QUARTERSPEED_LEVEL 6 - -// use 2.0 A as the ceiling, 2.5 A only for turbo -// start both ramps at the bottom; even moon throws a long way on the GT -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL POWER_80PX -// 1 23 46 [69] 92 115 138 -#define RAMP_DISCRETE_FLOOR 1 -#define RAMP_DISCRETE_CEIL POWER_80PX -#define RAMP_DISCRETE_STEPS 7 - -// GT can handle heat well, so don't limit simple mode -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL -#define SIMPLE_UI_STEPS 5 - -// smoother, more wobbly candle -#define CANDLE_AMPLITUDE 33 - -// turbo (i.e. "giggles" mode), low, tactical strobe -#define TACTICAL_LEVELS 150,30,(RAMP_SIZE+2) - -// stop panicking at 80% power, this light has plenty of thermal mass -#define THERM_FASTER_LEVEL POWER_80PX // throttle back faster when high - -// don't blink during ramp, it's irrelevant and annoying on this light -#undef BLINK_AT_RAMP_CEIL -#undef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_FLOOR - -// too big, turn off extra features -//#undef USE_TACTICAL_MODE -#undef USE_SOS_MODE - diff --git a/spaghetti-monster/anduril/cfg-blf-lantern-t1616.h b/spaghetti-monster/anduril/cfg-blf-lantern-t1616.h deleted file mode 100644 index fc02619..0000000 --- a/spaghetti-monster/anduril/cfg-blf-lantern-t1616.h +++ /dev/null @@ -1,110 +0,0 @@ -// BLF Lantern config options for Anduril using the Attiny1616 -// Copyright (C) 2021-2023 (original author TBD), Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0622" -#include "hwdef-blf-lt1-t1616.h" -// ATTINY: 1616 - -// the button lights up -#define USE_INDICATOR_LED -// the button is visible while main LEDs are on -#define USE_INDICATOR_LED_WHILE_RAMPING -// off mode: low (1) -// lockout: blinking (3) -#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) - -// channel modes... -// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO -#define DEFAULT_CHANNEL_MODE CM_AUTO -#define DEFAULT_BLINK_CHANNEL CM_BOTH - -#define CONFIG_WAITING_CHANNEL CM_BOTH -#define CONFIG_BLINK_CHANNEL CM_BOTH - -// blink numbers on the main LEDs by default (but allow user to change it) -#define DEFAULT_BLINK_CHANNEL CM_BOTH - -// how much to increase total brightness at middle tint -// (0 = 100% brightness, 64 = 200% brightness) -//#define TINT_RAMPING_CORRECTION 26 // prototype, 140% -#define TINT_RAMPING_CORRECTION 10 // production model, 115% - -#define RAMP_SIZE 150 -// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits) -// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) -// level_calc.py 3.333 1 150 7135 32 0.2 600 --pwm 32640 -#define PWM1_LEVELS 4,35,38,41,45,50,55,61,67,74,82,91,100,110,121,133,146,160,175,192,209,227,247,268,291,314,340,366,395,424,456,489,524,560,599,639,681,726,772,820,871,924,979,1036,1096,1158,1222,1289,1359,1431,1506,1584,1664,1747,1834,1923,2015,2111,2209,2311,2416,2524,2636,2751,2870,2992,3118,3247,3380,3518,3659,3803,3952,4105,4262,4423,4589,4759,4933,5111,5294,5482,5674,5871,6073,6279,6491,6707,6928,7155,7386,7623,7865,8113,8365,8624,8888,9157,9432,9713,10000,10292,10591,10895,11206,11523,11846,12175,12511,12853,13202,13557,13919,14287,14663,15045,15434,15830,16233,16644,17061,17486,17919,18358,18805,19260,19723,20193,20671,21156,21650,22152,22662,23180,23706,24241,24784,25335,25895,26464,27041,27627,28222,28826,29439,30060,30691,31332,31981,32640 - -#define DEFAULT_LEVEL 75 -#define MAX_1x7135 75 -#define HALFSPEED_LEVEL 0 // always use tint ramping correction -#define QUARTERSPEED_LEVEL 2 // quarter speed at level 1, full speed at 2+ -//#undef USE_DYNAMIC_UNDERCLOCKING // makes huge bumps in the ramp - -#define USE_SET_LEVEL_GRADUALLY - - -// override default ramp style -#undef RAMP_STYLE -#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped -// set floor and ceiling as far apart as possible -// because this lantern isn't overpowered -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 150 -#define RAMP_DISCRETE_FLOOR 1 -#define RAMP_DISCRETE_CEIL 150 -#define RAMP_DISCRETE_STEPS 7 - -// LT1 can handle heat well, so don't limit simple mode -#define SIMPLE_UI_FLOOR 10 -#define SIMPLE_UI_CEIL 150 -#define SIMPLE_UI_STEPS 5 - -// Allow 3C (or 6C) in Simple UI (toggle smooth or stepped ramping) -#define USE_SIMPLE_UI_RAMPING_TOGGLE - -// allow Aux Config and Strobe Modes in Simple UI -#define USE_EXTENDED_SIMPLE_UI - -#define USE_SOS_MODE -#define USE_SOS_MODE_IN_BLINKY_GROUP - -// the default of 26 looks a bit flat, so increase it -#define CANDLE_AMPLITUDE 40 - -#define USE_POLICE_COLOR_STROBE_MODE -#define POLICE_COLOR_STROBE_CH1 CM_CH1 -#define POLICE_COLOR_STROBE_CH2 CM_CH2 -// aux red + aux blue are the correct colors, but are dim -//#define POLICE_COLOR_STROBE_CH1 CM_AUXRED -//#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU - -#undef TACTICAL_LEVELS -#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe - -// party strobe, tac strobe, police, lightning, candle, bike -#define DEFAULT_STROBE_CHANNELS CM_BOTH,CM_BOTH,CM_BOTH,CM_AUTO,CM_AUTO,CM_AUTO - -// the sensor (attiny1616) is nowhere near the emitters -// so thermal regulation can't work -#ifdef USE_THERMAL_REGULATION -#undef USE_THERMAL_REGULATION -#endif - -// don't blink while ramping -#ifdef BLINK_AT_RAMP_FLOOR -#undef BLINK_AT_RAMP_FLOOR -#endif -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif -// except the top... blink at the top -#ifndef BLINK_AT_RAMP_CEIL -#define BLINK_AT_RAMP_CEIL -#endif - -// for consistency with other models -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/cfg-blf-lantern.h b/spaghetti-monster/anduril/cfg-blf-lantern.h deleted file mode 100644 index 53c3203..0000000 --- a/spaghetti-monster/anduril/cfg-blf-lantern.h +++ /dev/null @@ -1,105 +0,0 @@ -// BLF Lantern config options for Anduril -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0621" -#include "hwdef-blf-lt1.h" -// ATTINY: 85 - -// the button lights up -#define USE_INDICATOR_LED -// the button is visible while main LEDs are on -#define USE_INDICATOR_LED_WHILE_RAMPING -// off mode: low (1) -// lockout: blinking (3) -#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) - -// channel modes... -// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO -#define DEFAULT_CHANNEL_MODE CM_AUTO -//#define DEFAULT_BLINK_CHANNEL CM_BOTH // takes too much space - -// how much to increase total brightness at middle tint -// (0 = 100% brightness, 64 = 200% brightness) -//#define TINT_RAMPING_CORRECTION 26 // prototype, 140% -//#define TINT_RAMPING_CORRECTION 10 // production model, 115% -#define TINT_RAMPING_CORRECTION 0 // none - -#define RAMP_SIZE 150 -// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits) -// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) -// level_calc.py 3.333 1 150 7135 32 0.2 600 --pwm 32640 -#define PWM1_LEVELS 32,35,38,41,45,50,55,61,67,74,82,91,100,110,121,133,146,160,175,192,209,227,247,268,291,314,340,366,395,424,456,489,524,560,599,639,681,726,772,820,871,924,979,1036,1096,1158,1222,1289,1359,1431,1506,1584,1664,1747,1834,1923,2015,2111,2209,2311,2416,2524,2636,2751,2870,2992,3118,3247,3380,3518,3659,3803,3952,4105,4262,4423,4589,4759,4933,5111,5294,5482,5674,5871,6073,6279,6491,6707,6928,7155,7386,7623,7865,8113,8365,8624,8888,9157,9432,9713,10000,10292,10591,10895,11206,11523,11846,12175,12511,12853,13202,13557,13919,14287,14663,15045,15434,15830,16233,16644,17061,17486,17919,18358,18805,19260,19723,20193,20671,21156,21650,22152,22662,23180,23706,24241,24784,25335,25895,26464,27041,27627,28222,28826,29439,30060,30691,31332,31981,32640 - -#define DEFAULT_LEVEL 75 -#define MAX_1x7135 75 -#define HALFSPEED_LEVEL 44 -#define QUARTERSPEED_LEVEL 34 -#undef USE_DYNAMIC_UNDERCLOCKING // makes huge bumps in the ramp - -#define USE_SMOOTH_STEPS -//#define USE_SET_LEVEL_GRADUALLY - -// the default of 26 looks a bit flat, so increase it -#define CANDLE_AMPLITUDE 40 - -// override default ramp style -#undef RAMP_STYLE -#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped -// set floor and ceiling as far apart as possible -// because this lantern isn't overpowered -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 150 -#define RAMP_DISCRETE_FLOOR 1 -#define RAMP_DISCRETE_CEIL 150 -#define RAMP_DISCRETE_STEPS 7 - -// LT1 can handle heat well, so don't limit simple mode -#define SIMPLE_UI_FLOOR 10 -#define SIMPLE_UI_CEIL 150 -#define SIMPLE_UI_STEPS 5 - -// Allow 3C in Simple UI for switching between smooth and stepped ramping -#define USE_SIMPLE_UI_RAMPING_TOGGLE -#define USE_EXTENDED_SIMPLE_UI - -// also at Sofirn's request, enable 2 click turbo (Anduril 1 style) -#define DEFAULT_2C_STYLE 1 - - -// the sensor (attiny85) is nowhere near the emitters -// so thermal regulation can't work -#ifdef USE_THERMAL_REGULATION -#undef USE_THERMAL_REGULATION -#endif - -// don't blink while ramping -#ifdef BLINK_AT_RAMP_FLOOR -#undef BLINK_AT_RAMP_FLOOR -#endif -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif -#ifdef BLINK_AT_RAMP_CEIL -#undef BLINK_AT_RAMP_CEIL -#endif - -// too big, turn off extra features -//#undef USE_STEPPED_TINT_RAMPING -#undef USE_MOMENTARY_MODE -#undef USE_TACTICAL_MODE -#undef USE_SOS_MODE -//#undef USE_SIMPLE_UI -//#undef USE_BEACON_MODE -//#undef USE_RAMP_SPEED_CONFIG -#undef USE_RAMP_AFTER_MOON_CONFIG -#undef USE_2C_STYLE_CONFIG -#undef USE_VOLTAGE_CORRECTION -//#undef USE_CHANNEL_PER_STROBE -// party strobe, tac strobe, lightning, candle, bike -#define DEFAULT_STROBE_CHANNELS CM_BOTH,CM_BOTH,CM_AUTO,CM_AUTO,CM_AUTO - -// for consistency with other models -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/cfg-blf-q8-t1616.h b/spaghetti-monster/anduril/cfg-blf-q8-t1616.h deleted file mode 100644 index 9f0402c..0000000 --- a/spaghetti-monster/anduril/cfg-blf-q8-t1616.h +++ /dev/null @@ -1,82 +0,0 @@ -// BLF Q8 config options for Anduril using the Attiny1616 -// Copyright (C) 2021-2023 gchart, Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0613" -#include "hwdef-blf-q8-t1616.h" -#include "wurkkos-cfg.h" // Sofirn lights are closely related to Wurkkos -// ATTINY: 1616 - -// the button lights up -#define USE_INDICATOR_LED -// the button is visible while main LEDs are on -#define USE_INDICATOR_LED_WHILE_RAMPING -// off mode: low (1) -// lockout: blinking (3) -#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) - -// copied from Wurkkos TS25 ramp -#define RAMP_SIZE 150 -// 7135 at 75/150 -// level_calc.py 5.7895 2 150 7135 1 0.1 130 FET 1 10 3000 --pwm dyn:74:4096:255:3 -// (with some manual tweaks) -#define PWM1_LEVELS 1,1,2,3,3,4,5,6,7,8,9,11,12,13,15,16,18,19,21,23,26,27,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,71,74,76,78,80,82,85,87,90,93,96,100,103,107,112,116,122,127,133,140,147,154,163,171,182,192,203,215,228,241,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -// non-zero part of FET channel calculated with: -// level_calc.py 3 1 75 7135 1 200 3000 -// (FIXME? there's a visible bump when the FET kicks in, even with just 1/255) -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,7,8,10,11,13,14,16,17,19,21,22,24,26,28,30,32,34,37,39,41,44,46,48,51,54,56,59,62,65,68,71,74,77,81,84,87,91,94,98,102,106,110,114,118,122,126,130,135,139,144,148,153,158,163,168,173,178,184,189,195,200,206,212,218,224,230,236,242,248,255 -#define PWM_TOPS 4095,2701,3200,3586,2518,2778,2834,2795,2705,2587,2455,2582,2412,2247,2256,2091,2062,1907,1860,1802,1737,1605,1542,1477,1412,1347,1284,1222,1162,1105,1050,997,946,898,853,810,768,730,693,658,625,594,564,536,503,485,462,439,418,398,384,366,353,340,327,319,307,298,292,284,280,273,269,266,263,260,258,256,256,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 - -#define MAX_1x7135 75 -#define DEFAULT_LEVEL 50 -#define MIN_THERM_STEPDOWN 60 -#define HALFSPEED_LEVEL 20 -#define QUARTERSPEED_LEVEL 5 - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 150 -// 20 38 56 [75] 93 111 130 -#define RAMP_DISCRETE_FLOOR 20 -#define RAMP_DISCRETE_CEIL 130 -#define RAMP_DISCRETE_STEPS 7 - -// at Sofirn's request, use max (150) for the Simple UI ceiling -#define SIMPLE_UI_FLOOR 1 -#define SIMPLE_UI_CEIL 150 -#define SIMPLE_UI_STEPS 5 - -// also at Sofirn's request, enable 2 click turbo (Anduril 1 style) -#define DEFAULT_2C_STYLE 1 - -// enable SOS in the blinkies group -#define USE_SOS_MODE -#define USE_SOS_MODE_IN_BLINKY_GROUP - -// Allow 3C in Simple UI for switching between smooth and stepped ramping -#define USE_SIMPLE_UI_RAMPING_TOGGLE - -// allow Aux Config and Strobe Modes in Simple UI -#define USE_EXTENDED_SIMPLE_UI - -// stop panicking at ~75% power or ~3000 lm, this light has high thermal mass -#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high - -// show each channel while it scroll by in the menu -#define USE_CONFIG_COLORS - -// blink numbers on the aux LEDs by default -#define DEFAULT_BLINK_CHANNEL CM_AUX - -// the default of 26 looks a bit rough, so increase it to make it smoother -#define CANDLE_AMPLITUDE 33 - -// don't blink during the ramp; the button LED brightness is sufficient -// to indicate which power channel(s) are being used -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - -// enable factory reset on 13H without loosening tailcap (required) -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/cfg-blf-q8.h b/spaghetti-monster/anduril/cfg-blf-q8.h deleted file mode 100644 index 29dbcb7..0000000 --- a/spaghetti-monster/anduril/cfg-blf-q8.h +++ /dev/null @@ -1,71 +0,0 @@ -// BLF Q8 config options for Anduril -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0611" -#include "hwdef-blf-q8.h" - -#define RAMP_SIZE 150 - -// the button lights up -#define USE_INDICATOR_LED -// the button is visible while main LEDs are on -#define USE_INDICATOR_LED_WHILE_RAMPING -// off mode: high (2) -// lockout: blinking (3) -#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 2) - -// copied from Emisar D4 ramp -// ../../bin/level_calc.py 1 65 7135 1 0.8 150 -// ... mixed with this: -// ../../bin/level_calc.py 2 150 7135 4 0.33 150 FET 1 10 1500 -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255 - -#define MAX_1x7135 65 -#define DEFAULT_LEVEL 65 -#define HALFSPEED_LEVEL 14 -#define QUARTERSPEED_LEVEL 5 - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 120 -// 10 28 46 [65] 83 101 120 -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// at Sofirn's request, use max (150) for the Simple UI ceiling -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL 150 -#define SIMPLE_UI_STEPS 5 - -// also at Sofirn's request, enable 2 click turbo (Anduril 1 style) -#define DEFAULT_2C_STYLE 1 - -// enable SOS in the blinkies group -#define USE_SOS_MODE -#define USE_SOS_MODE_IN_BLINKY_GROUP - -// Allow 3C in Simple UI for switching between smooth and stepped ramping -#define USE_SIMPLE_UI_RAMPING_TOGGLE -#define USE_EXTENDED_SIMPLE_UI - -// stop panicking at ~75% power or ~3000 lm, this light has high thermal mass -#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high - -// don't blink mid-ramp -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - -#define USE_SMOOTH_STEPS - -// too big, remove stuff to make room -#undef USE_SOS_MODE -//#undef USE_RAMP_AFTER_MOON_CONFIG -//#undef USE_RAMP_SPEED_CONFIG -//#undef USE_VOLTAGE_CORRECTION -//#undef USE_2C_STYLE_CONFIG -//#undef USE_TACTICAL_STROBE_MODE -#undef USE_TACTICAL_MODE diff --git a/spaghetti-monster/anduril/cfg-emisar-2ch-fet.h b/spaghetti-monster/anduril/cfg-emisar-2ch-fet.h deleted file mode 100644 index 4686483..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-2ch-fet.h +++ /dev/null @@ -1,113 +0,0 @@ -// Emisar 2-channel generic (plus FET) config options for Anduril (based on Noctigon K9.3) -// Copyright (C) 2021-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0136" -#include "hwdef-emisar-2ch-fet.h" -#include "hank-cfg.h" -// ATTINY: 1634 - -// 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 -// TODO: the whole "indicator LED" thing needs to be refactored into -// "aux LED(s)" and "button LED(s)" since they work a bit differently -// enabling this option breaks the button LED on D4v2.5 -#ifdef USE_INDICATOR_LED_WHILE_RAMPING -#undef USE_INDICATOR_LED_WHILE_RAMPING -#endif - -// channel modes... -// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO -// enable max brightness out of the box -#define DEFAULT_CHANNEL_MODE CM_BOTH - -#define USE_CONFIG_COLORS - -// blink numbers on the main LEDs by default (but allow user to change it) -#define DEFAULT_BLINK_CHANNEL CM_BOTH - -#define POLICE_COLOR_STROBE_CH1 CM_CH1 -#define POLICE_COLOR_STROBE_CH2 CM_CH2 - -// how much to increase total brightness at middle tint -// (0 = 100% brightness, 64 = 200% brightness) -#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it - - -// channel 1 / main LEDs (w/ DD FET) -// output: unknown, 1750 lm? -// FET: unknown, 3000 lm? -// channel 2 -// output: unknown, 1750 lm? -// combined: 4000 to 5000 lm? -#define RAMP_SIZE 150 - -// linear+FET ramp: maxreg at 140/150 -// level_calc.py 5.01 2 150 7135 1 0.1 1924 FET 1 10 2600 --pwm dyn:68:4096:255:3 --clock 8:16:0 -// linear segment -#define PWM1_LEVELS 1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7,7,8,8,8,9,9,9,10,10,11,12,13,13,14,15,16,17,17,18,19,20,21,22,23,25,26,27,28,30,31,33,34,36,37,39,41,43,44,46,48,50,53,55,57,60,62,65,67,70,73,76,79,82,85,88,92,95,99,103,106,110,114,119,123,127,132,137,142,147,152,157,162,168,174,179,185,192,198,204,211,218,225,232,240,247,255,255,255,255,255,255,255,255,255,255,255 -// DD FET segment -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,68,93,118,144,171,198,226,255 -// PWM TOPS values -#define PWM3_LEVELS 4096,3831,3570,3314,3063,2817,2577,2342,2115,1894,1682,1477,1281,2237,2005,1784,1576,1380,1196,1111,1026,1442,1348,1215,1091,976,871,774,1031,942,860,784,714,650,591,538,652,602,555,513,473,437,403,372,343,398,370,345,322,299,278,311,292,273,255,278,261,245,263,247,232,246,231,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 - -// linear-only ramp -// level_calc.py 3.11 1 150 7135 1 0.1 1750 --pwm dyn:64:4096:255:3 --clock 8:16:0 -#define PWM4_LEVELS 1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,5,5,6,6,6,7,7,7,8,8,8,9,9,9,10,10,10,10,10,10,11,11,11,11,11,11,11,12,12,12,12,12,12,13,13,13,14,14,14,15,15,16,17,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,33,34,35,37,38,39,41,42,44,45,47,48,50,52,53,55,57,59,61,63,65,67,69,71,73,75,77,79,82,84,86,89,91,94,97,99,102,105,107,110,113,116,119,122,125,128,132,135,138,141,145,148,152,156,159,163,167,170,174,178,182,186,190,195,199,203,208,212,217,221,226,230,235,240,245,250,255 -// PWM_TOPS values for linear-only ramp -#define PWM5_LEVELS 4096,3681,3247,2794,2328,1856,2937,2393,1860,2690,2273,1875,2281,1959,1658,1893,1646,1774,1569,1381,1466,1309,1166,1224,1104,996,1033,942,858,882,810,746,687,634,586,604,561,522,487,454,425,397,409,385,362,341,321,302,311,295,279,286,271,257,263,250,255,258,246,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 - -#define DEFAULT_LEVEL 75 -#define MAX_1x7135 140 -#define MAX_Nx7135 MAX_1x7135 -#define HALFSPEED_LEVEL 16 -#define QUARTERSPEED_LEVEL 8 - -#define RAMP_SMOOTH_FLOOR 10 // level 1 is unreliable (?) -#define RAMP_SMOOTH_CEIL 140 -// 10, 31, 53, [75], 96, 118, [140] -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// safe limit highest regulated power (no FET or turbo) -// 10, 42, [75], 107, 140 -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL -#define SIMPLE_UI_STEPS 5 - -// stop panicking at ~1500 lm -#define THERM_FASTER_LEVEL 130 -#define MIN_THERM_STEPDOWN 65 // should be above highest dyn_pwm level - -#define USE_POLICE_COLOR_STROBE_MODE -#undef TACTICAL_LEVELS -#define TACTICAL_LEVELS 140,30,(RAMP_SIZE+3) // high, low, police strobe - -// use the brightest setting for strobe -#define STROBE_BRIGHTNESS MAX_LEVEL -// slow down party strobe; this driver can't pulse for 1ms or less -#define PARTY_STROBE_ONTIME 2 -// TODO: change speed per channel mode -// (the FET is really fast, but the regulator is not) -//#undef PARTY_STROBE_ONTIME - -// the default of 26 looks a bit flat, so increase it -#define CANDLE_AMPLITUDE 33 - -// the power regulator is a bit slow, so push it harder for a quick response from off -#define DEFAULT_JUMP_START_LEVEL 40 -#define BLINK_BRIGHTNESS 45 -#define BLINK_ONCE_TIME 12 // longer blink, since main LEDs are slow - -#define THERM_CAL_OFFSET 5 - -// don't blink while ramping -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - diff --git a/spaghetti-monster/anduril/cfg-emisar-2ch.h b/spaghetti-monster/anduril/cfg-emisar-2ch.h deleted file mode 100644 index 4c3a329..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-2ch.h +++ /dev/null @@ -1,103 +0,0 @@ -// Emisar 2-channel generic config options for Anduril -// Copyright (C) 2021-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0135" -#include "hwdef-emisar-2ch.h" -#include "hank-cfg.h" -// ATTINY: 1634 - -// 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 -// TODO: the whole "indicator LED" thing needs to be refactored into -// "aux LED(s)" and "button LED(s)" since they work a bit differently -// enabling this option breaks the button LED on D4v2.5 -#ifdef USE_INDICATOR_LED_WHILE_RAMPING -#undef USE_INDICATOR_LED_WHILE_RAMPING -#endif - -// channel modes... -// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO -// enable max brightness out of the box -#define DEFAULT_CHANNEL_MODE CM_BLEND - -#define USE_CONFIG_COLORS - -// blink numbers on the main LEDs by default (but allow user to change it) -#define DEFAULT_BLINK_CHANNEL CM_BLEND - -#define POLICE_COLOR_STROBE_CH1 CM_CH1 -#define POLICE_COLOR_STROBE_CH2 CM_CH2 - -// how much to increase total brightness at middle tint -// (0 = 100% brightness, 64 = 200% brightness) -#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it - - -// channel 1 -// output: unknown, 2000 lm? -// FET: absent / unused -// channel 2 -// output: unknown, 2000 lm? -#define RAMP_SIZE 150 - -// abstract ramp (power is split between both sets of LEDs) -// 1-130: 0 to 100% power -// level_calc.py 5.01 1 130 7135 2 0.2 2000 --pwm dyn:64:16383:511 -// 131 to 150: 101% to 200% power -// level_calc.py 6.44 1 150 7135 1 0.2 2000 --pwm dyn:74:16383:1022 -#define PWM1_LEVELS 1,1,1,2,2,2,3,3,4,5,5,6,7,8,9,10,11,12,13,15,16,18,19,21,22,24,26,28,29,31,33,35,37,40,42,44,46,48,50,52,54,56,58,59,61,62,63,64,65,66,66,66,66,65,64,62,60,58,54,50,46,41,35,28,20,21,22,24,25,26,27,29,30,32,33,35,37,38,40,42,44,46,48,50,53,55,57,60,63,65,68,71,74,77,80,83,87,90,94,98,102,106,110,114,118,123,128,132,137,142,148,153,159,164,170,176,183,189,196,202,209,216,224,231,239,247,255,263,272,281,290,299,309,318,328,339,349,360,371,382,394,406,418,430,443,456,469,483,497,511 -#define PWM_TOPS 16383,13650,10715,15388,11902,8195,12771,9834,12258,13423,11192,11947,12284,12363,12271,12064,11775,11428,11039,11469,10973,11132,10595,10601,10054,9971,9833,9655,9122,8923,8704,8473,8232,8196,7932,7668,7408,7152,6901,6656,6417,6186,5961,5647,5444,5163,4899,4653,4422,4206,3941,3697,3470,3210,2971,2707,2466,2245,1968,1717,1489,1251,1005,756,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511 -// max "200% power" ramp and tops -//#define PWM2_LEVELS 2,2,2,3,3,4,4,5,6,7,8,9,10,11,13,14,16,17,19,21,23,25,28,30,33,35,38,41,44,47,50,54,57,60,64,67,71,74,78,81,84,88,91,94,97,99,101,103,105,106,107,107,107,106,105,102,99,95,90,84,77,68,58,47,34,36,38,40,42,44,47,49,52,54,57,60,63,66,69,73,76,80,83,87,91,96,100,104,109,114,119,124,130,135,141,147,153,160,166,173,180,187,195,203,211,219,228,236,245,255,264,274,285,295,306,317,329,340,353,365,378,391,405,419,433,448,463,479,495,511,530,550,570,591,612,634,657,680,705,730,755,782,809,837,865,895,925,957,989,1022 -//#define PWM3_LEVELS 16383,13234,9781,13826,9593,13434,9973,12021,12900,13193,13150,12899,12508,12023,12666,11982,12181,11422,11393,11247,11018,10731,10826,10434,10365,9927,9767,9565,9332,9076,8806,8693,8395,8096,7928,7626,7439,7143,6948,6665,6393,6203,5946,5700,5465,5187,4926,4681,4451,4195,3957,3700,3463,3213,2983,2718,2476,2231,1986,1742,1501,1245,997,756,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511 -#define DEFAULT_LEVEL 70 -#define MAX_1x7135 150 -#define HALFSPEED_LEVEL 10 -#define QUARTERSPEED_LEVEL 2 - -#define RAMP_SMOOTH_FLOOR 10 // level 1 is unreliable (?) -#define RAMP_SMOOTH_CEIL 130 -// 10, 30, 50, [70], 90, 110, [130] -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// safe limit highest regulated power (no FET or turbo) -// 10, 40, [70], 100, 130 -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL -#define SIMPLE_UI_STEPS 5 - -// stop panicking at ~1500 lm -#define THERM_FASTER_LEVEL 130 -#define MIN_THERM_STEPDOWN 65 // should be above highest dyn_pwm level - -#define USE_POLICE_COLOR_STROBE_MODE -#undef TACTICAL_LEVELS -#define TACTICAL_LEVELS 130,30,(RAMP_SIZE+3) // high, low, police strobe - -// use the brightest setting for strobe -#define STROBE_BRIGHTNESS MAX_LEVEL -// slow down party strobe; this driver can't pulse for 1ms or less -#define PARTY_STROBE_ONTIME 2 - -// the default of 26 looks a bit flat, so increase it -#define CANDLE_AMPLITUDE 33 - -// the power regulator is a bit slow, so push it harder for a quick response from off -#define DEFAULT_JUMP_START_LEVEL 21 -#define BLINK_BRIGHTNESS 40 -#define BLINK_ONCE_TIME 12 // longer blink, since main LEDs are slow - -#define THERM_CAL_OFFSET 5 - -// don't blink while ramping -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - diff --git a/spaghetti-monster/anduril/cfg-emisar-d1.h b/spaghetti-monster/anduril/cfg-emisar-d1.h deleted file mode 100644 index c81171c..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-d1.h +++ /dev/null @@ -1,20 +0,0 @@ -// Emisar D1 config options for Anduril -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// same as Emisar D4, mostly -#include "hwdef-emisar-d4.h" -#include "cfg-emisar-d4.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0121" - -// safe limit ~50% power -#undef SIMPLE_UI_CEIL -#define SIMPLE_UI_CEIL 120 - -// stop panicking at ~75% power or ~1000 lm (D1 has a decent power-to-thermal-mass ratio) -#ifdef THERM_FASTER_LEVEL -#undef THERM_FASTER_LEVEL -#endif -#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high diff --git a/spaghetti-monster/anduril/cfg-emisar-d18-219.h b/spaghetti-monster/anduril/cfg-emisar-d18-219.h deleted file mode 100644 index 126e9f4..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-d18-219.h +++ /dev/null @@ -1,18 +0,0 @@ -// Emisar D18 (FET+13+1) reduced-FET config options for Anduril -// Copyright (C) 2019-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#include "cfg-emisar-d18.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0142" - -// don't turn off first channels at turbo level -#undef PWM1_LEVELS -#undef PWM2_LEVELS -#define PWM1_LEVELS 1,1,2,2,3,4,4,5,6,7,8,9,10,11,15,16,18,20,22,24,26,28,30,33,36,39,43,47,51,56,61,66,72,78,85,92,99,107,116,125,135,145,156,168,180,194,208,222,238,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,10,11,13,14,16,18,19,21,23,26,28,30,33,35,38,41,44,47,51,54,58,62,66,70,75,79,84,90,95,101,106,112,119,126,133,140,147,155,164,172,181,190,200,210,221,232,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -// 85% FET power -#undef PWM3_LEVELS -#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,6,8,10,12,13,16,17,20,22,24,27,29,32,34,38,40,44,46,50,53,57,60,64,68,72,76,80,85,89,94,98,103,108,114,119,125,131,137,143,150,156,163,170,177,185,193,200,209,217 - diff --git a/spaghetti-monster/anduril/cfg-emisar-d18.h b/spaghetti-monster/anduril/cfg-emisar-d18.h deleted file mode 100644 index 3e5d3ae..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-d18.h +++ /dev/null @@ -1,61 +0,0 @@ -// Emisar D18 (FET+13+1) config options for Anduril -// Copyright (C) 2019-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0141" -#include "hwdef-emisar-d18.h" -#include "hank-cfg.h" - -#define RAMP_SIZE 150 - -// level_calc.py seventh 3 150 7135 1 1.4 117.99 7135 6 1 1706.86 FET 3 10 13000 -// (designed to make 1x hit at level 50, and Nx hit at level 100) -#define PWM1_LEVELS 1,1,2,2,3,4,4,5,6,7,8,9,10,11,15,16,18,20,22,24,26,28,30,33,36,39,43,47,51,56,61,66,72,78,85,92,99,107,116,125,135,145,156,168,180,194,208,222,238,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,10,11,13,14,16,18,19,21,23,26,28,30,33,35,38,41,44,47,51,54,58,62,66,70,75,79,84,90,95,101,106,112,119,126,133,140,147,155,164,172,181,190,200,210,221,232,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,7,9,11,13,15,18,20,23,25,28,31,34,37,40,44,47,51,54,58,62,66,70,75,79,84,89,94,99,104,110,115,121,127,134,140,147,154,161,168,176,183,191,200,208,217,226,235,245,255 - -#define DEFAULT_LEVEL 50 -#define MAX_1x7135 50 -#define MAX_Nx7135 100 -#define HALFSPEED_LEVEL 15 -#define QUARTERSPEED_LEVEL 6 - -// start at ~2000 lm after battery change, not ~150 lm (at Emisar's request) -//#define DEFAULT_LEVEL MAX_Nx7135 - -// go up to ~4000 lm -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 117 -// 20 36 52 68 84 [100] 117 -#define RAMP_DISCRETE_FLOOR 20 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// safe limit ~20% power / max regulated -// 20 40 60 80 100 -#define SIMPLE_UI_FLOOR 20 -#define SIMPLE_UI_CEIL MAX_Nx7135 -#define SIMPLE_UI_STEPS 5 - -// only blink at max regulated level and ceiling -//#define BLINK_AT_RAMP_MIDDLE -//#define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135 -#define BLINK_AT_RAMP_CEIL -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - -// stop panicking at about ~40% power or ~5000 lm -#define THERM_FASTER_LEVEL 125 - -// enable extra features -#define USE_SMOOTH_STEPS - -// for compatibility with other models -#define USE_SOFT_FACTORY_RESET - -// too big, turn off extra features -#undef USE_TACTICAL_MODE -#undef USE_SOS_MODE - diff --git a/spaghetti-monster/anduril/cfg-emisar-d1s.h b/spaghetti-monster/anduril/cfg-emisar-d1s.h deleted file mode 100644 index 8b70a5d..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-d1s.h +++ /dev/null @@ -1,23 +0,0 @@ -// Emisar D1S config options for Anduril -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// same as Emisar D4, mostly -#include "hwdef-emisar-d4.h" -#include "cfg-emisar-d4.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0122" - -// safe limit ~50% power -#undef SIMPLE_UI_CEIL -#define SIMPLE_UI_CEIL 120 - -// stop panicking at ~90% power or ~1200 lm (D1S has a good power-to-thermal-mass ratio) -#ifdef THERM_FASTER_LEVEL -#undef THERM_FASTER_LEVEL -#endif -#define THERM_FASTER_LEVEL 144 // throttle back faster when high - -// too big, turn off extra features -//#undef USE_TACTICAL_MODE diff --git a/spaghetti-monster/anduril/cfg-emisar-d1v2-7135-fet.h b/spaghetti-monster/anduril/cfg-emisar-d1v2-7135-fet.h deleted file mode 100644 index 4022ba6..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-d1v2-7135-fet.h +++ /dev/null @@ -1,32 +0,0 @@ -// Emisar D1v2 (7135+FET) config options for Anduril -// Copyright (C) 2020-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// ATTINY: 1634 -// same as Emisar D4v2, mostly -// (was only made for a short time, not many people have one) -#include "cfg-emisar-d4v2.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0123" - -// some models use a simple button LED, others use RGB... -// ... so include support for both -#define USE_BUTTON_LED -// the aux LEDs are in the button, so use them while main LEDs are on -// (early short run had no button LEDs at all, later run uses linear+FET instead, -// so it's unlikely that anyone needs this, but it doesn't hurt anything) -#define USE_AUX_RGB_LEDS -#define USE_AUX_RGB_LEDS_WHILE_ON 25 -#define USE_INDICATOR_LED_WHILE_RAMPING - -// safe limit ~50% power -#undef SIMPLE_UI_CEIL -#define SIMPLE_UI_CEIL 120 - -// stop panicking at ~75% power or ~1000 lm (D1 has a decent power-to-thermal-mass ratio) -#ifdef THERM_FASTER_LEVEL -#undef THERM_FASTER_LEVEL -#endif -#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high - diff --git a/spaghetti-monster/anduril/cfg-emisar-d1v2-linear-fet.h b/spaghetti-monster/anduril/cfg-emisar-d1v2-linear-fet.h deleted file mode 100644 index 28c57f8..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-d1v2-linear-fet.h +++ /dev/null @@ -1,30 +0,0 @@ -// Emisar D1v2 (linear+FET) config options for Anduril -// (2022 re-issue / update of old D1) -// Copyright (C) 2022-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// ATTINY: 1634 -// similar to a Noctigon KR4, sort of -#include "cfg-noctigon-kr4.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0124" - -// some models use a simple button LED, others use RGB... -// ... so include support for both -#define USE_BUTTON_LED -// the aux LEDs are in the button, so use them while main LEDs are on -#define USE_AUX_RGB_LEDS -#define USE_AUX_RGB_LEDS_WHILE_ON 25 -#define USE_INDICATOR_LED_WHILE_RAMPING - -// safe limit: max regulated power -#undef SIMPLE_UI_CEIL -#define SIMPLE_UI_CEIL MAX_1x7135 - -// stop panicking at ~75% power or ~1000 lm (D1 has a decent power-to-thermal-mass ratio) -#ifdef THERM_FASTER_LEVEL -#undef THERM_FASTER_LEVEL -#endif -#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high - diff --git a/spaghetti-monster/anduril/cfg-emisar-d1v2-nofet.h b/spaghetti-monster/anduril/cfg-emisar-d1v2-nofet.h deleted file mode 100644 index 7f5bcc4..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-d1v2-nofet.h +++ /dev/null @@ -1,24 +0,0 @@ -// Emisar D1v2 (linear only, no DDFET) config options for Anduril -// (2022 re-issue / update of old D1) -// Copyright (C) 2022-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// ATTINY: 1634 -// similar to a Noctigon KR4, sort of -#include "cfg-noctigon-kr4-nofet.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0125" - -// some models use a simple button LED, others use RGB... -// ... so include support for both -#define USE_BUTTON_LED -// the aux LEDs are in the button, so use them while main LEDs are on -#define USE_AUX_RGB_LEDS -#define USE_AUX_RGB_LEDS_WHILE_ON 25 -#define USE_INDICATOR_LED_WHILE_RAMPING - -// safe limit: same as regular ramp -#undef SIMPLE_UI_CEIL -#define SIMPLE_UI_CEIL RAMP_SMOOTH_CEIL - diff --git a/spaghetti-monster/anduril/cfg-emisar-d4-219c.h b/spaghetti-monster/anduril/cfg-emisar-d4-219c.h deleted file mode 100644 index 65649e3..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-d4-219c.h +++ /dev/null @@ -1,17 +0,0 @@ -// Emisar D4-219C config options for Anduril -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// same as D4 but with FET modes limited to 80% power -// to avoid destroying the LEDs -#include "cfg-emisar-d4.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0112" - -// don't turn off first channel at turbo level -#undef PWM1_LEVELS -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -// 65% FET power -#undef PWM2_LEVELS -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,15,16,17,18,19,21,22,23,25,26,27,28,30,32,33,34,36,38,39,41,42,44,46,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,78,80,82,84,87,90,92,94,97,99,102,104,108,110,113,116,119,121,125,127,130,134,136,140,143,146,149,153,156,159,163,166 diff --git a/spaghetti-monster/anduril/cfg-emisar-d4.h b/spaghetti-monster/anduril/cfg-emisar-d4.h deleted file mode 100644 index 4b3ae5a..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-d4.h +++ /dev/null @@ -1,48 +0,0 @@ -// Emisar D4 config options for Anduril -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0111" -#include "hwdef-emisar-d4.h" -#include "hank-cfg.h" -// ATTINY: 85 - -#define RAMP_SIZE 150 - -// ../../bin/level_calc.py 1 65 7135 1 0.8 150 -// ... mixed with this: -// ../../bin/level_calc.py 2 150 7135 4 0.33 150 FET 1 10 1500 -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255 - -#define MAX_1x7135 65 -#define DEFAULT_LEVEL 65 -#define HALFSPEED_LEVEL 15 -#define QUARTERSPEED_LEVEL 6 - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 120 -// 10, 28, 46, [65], 83, 101, 120 -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// safe limit ~20% power -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL 100 -#define SIMPLE_UI_STEPS 5 - -// stop panicking at ~30% power or ~1200 lm -#define THERM_FASTER_LEVEL 105 - -// don't blink during ramp -#undef BLINK_AT_RAMP_MIDDLE - -// enable extra features -#define USE_SMOOTH_STEPS - -// too big, turn off extra features -//#undef USE_TACTICAL_MODE -#undef USE_SOS_MODE - diff --git a/spaghetti-monster/anduril/cfg-emisar-d4k-3ch.h b/spaghetti-monster/anduril/cfg-emisar-d4k-3ch.h deleted file mode 100644 index c39ac01..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-d4k-3ch.h +++ /dev/null @@ -1,106 +0,0 @@ -// Emisar D4K 3-channel config options for Anduril -// Copyright (C) 2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0151" -#include "hwdef-emisar-d4k-3ch.h" -#include "hank-cfg.h" -// ATTINY: 1634 - -// this light has three aux LED channels: R, G, B -#define USE_AUX_RGB_LEDS - -// turn on the aux LEDs while main LEDs are on -// (in case there's a RGB button) -#define USE_AUX_RGB_LEDS_WHILE_ON 40 -#define USE_INDICATOR_LED_WHILE_RAMPING - -// channel modes... -// CM_MAIN2, CM_LED3, CM_LED4, CM_ALL, -// CM_BLEND34A, CM_BLEND34B, CM_HSV, CM_AUTO3 -#define DEFAULT_CHANNEL_MODE CM_ALL - -#define FACTORY_RESET_WARN_CHANNEL CM_LED4 -#define FACTORY_RESET_SUCCESS_CHANNEL CM_MAIN2 - -#define CONFIG_WAITING_CHANNEL CM_LED3 -#define CONFIG_BLINK_CHANNEL CM_ALL - -// blink numbers on the main LEDs by default (but allow user to change it) -#define DEFAULT_BLINK_CHANNEL CM_MAIN2 - -// LEDs 3 and 4 make a nice police strobe -#define POLICE_COLOR_STROBE_CH1 CM_LED3 -#define POLICE_COLOR_STROBE_CH2 CM_LED4 -// aux red + aux blue are the correct colors, but are dim -//#define POLICE_COLOR_STROBE_CH1 CM_AUXRED -//#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU - -// how much to increase total brightness at middle tint -// (0 = 100% brightness, 64 = 200% brightness) -#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it - -// main 2 LEDs -// output: unknown, 2000 lm? -// LED 3 / 4 -// output: unknown, 1000 lm each? -#define RAMP_SIZE 150 -// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits) -// level_calc.py 5.01 1 150 7135 0 0.2 2000 --pwm 32640 -// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) -#define PWM1_LEVELS 0,1,2,3,4,5,6,7,9,10,12,14,17,19,22,25,28,32,36,41,45,50,56,62,69,76,84,92,101,110,121,132,143,156,169,184,199,215,232,251,270,291,313,336,360,386,414,442,473,505,539,574,612,651,693,736,782,829,880,932,987,1045,1105,1168,1233,1302,1374,1449,1527,1608,1693,1781,1873,1969,2068,2172,2279,2391,2507,2628,2753,2883,3018,3158,3303,3454,3609,3771,3938,4111,4289,4475,4666,4864,5068,5280,5498,5724,5957,6197,6445,6701,6965,7237,7518,7808,8106,8413,8730,9056,9392,9737,10093,10459,10835,11223,11621,12031,12452,12884,13329,13786,14255,14737,15232,15741,16262,16798,17347,17911,18489,19082,19691,20314,20954,21609,22281,22969,23674,24397,25137,25895,26671,27465,28279,29111,29963,30835,31727,32640 - -#define MIN_THERM_STEPDOWN 50 -#define DEFAULT_LEVEL 70 -#define MAX_1x7135 70 -// always run at 1/4th speed, because 4 kHz PWM is enough for this circuit -// and speed changes make a big visible bump -#define HALFSPEED_LEVEL 255 -#define QUARTERSPEED_LEVEL 255 - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 130 -// 10, 30, 50, [70], 90, 110, 130 -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// 10 40 [70] 100 130 -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL -#define SIMPLE_UI_STEPS 5 - -// stop panicking at ~1500 lm -#define THERM_FASTER_LEVEL 130 - -#define USE_POLICE_COLOR_STROBE_MODE -#undef TACTICAL_LEVELS -#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe - -// use the brightest setting for strobe -#define STROBE_BRIGHTNESS MAX_LEVEL -// slow down party strobe; this driver can't pulse for 1ms or less -#define PARTY_STROBE_ONTIME 2 -// #define STROBE_OFF_LEVEL 1 // nope, this makes strobe blurry -// bike strobe needs a longer pulse too? -//#define BIKE_STROBE_ONTIME 8 - -// the default of 26 looks a bit flat, so increase it -#define CANDLE_AMPLITUDE 33 - -// the power regulator is a bit slow, so push it harder for a quick response from off -#define DEFAULT_JUMP_START_LEVEL 21 -#define BLINK_BRIGHTNESS 50 -#define BLINK_ONCE_TIME 12 // longer blink, since main LEDs are slow - -#define THERM_CAL_OFFSET 5 - -// don't blink while ramping -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - -// for consistency with KR4 (not otherwise necessary though) -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/cfg-emisar-d4s-219c.h b/spaghetti-monster/anduril/cfg-emisar-d4s-219c.h deleted file mode 100644 index f86c1b1..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-d4s-219c.h +++ /dev/null @@ -1,17 +0,0 @@ -// Emisar D4S-219C config options for Anduril -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// same as D4S but with FET modes limited to 80% power -// to avoid destroying the LEDs -#include "cfg-emisar-d4s.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0132" - -// don't turn off first channel at turbo level -#undef PWM1_LEVELS -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12,13,14,15,16,17,18,19,21,22,23,25,26,27,29,31,32,34,36,38,40,42,44,46,49,51,54,56,59,62,65,68,71,74,78,81,85,89,93,97,101,106,110,115,120,125,130,136,141,147,153,160,166,173,180,187,195,202,210,219,227,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -// 65% FET power -#undef PWM2_LEVELS -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,17,18,19,21,23,24,25,27,28,30,32,34,36,38,39,41,43,45,47,50,52,54,57,60,62,65,67,70,73,76,79,82,86,89,92,95,99,103,106,110,114,119,123,127,132,136,141,145,151,156,161,166 diff --git a/spaghetti-monster/anduril/cfg-emisar-d4s.h b/spaghetti-monster/anduril/cfg-emisar-d4s.h deleted file mode 100644 index e966132..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-d4s.h +++ /dev/null @@ -1,49 +0,0 @@ -// Emisar D4S config options for Anduril -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0131" -#include "hwdef-emisar-d4s.h" -#include "hank-cfg.h" -// ATTINY: 85 - -// the button lights up (on some models) -#define USE_INDICATOR_LED -// the aux LEDs are behind the main LEDs -#ifdef USE_INDICATOR_LED_WHILE_RAMPING -#undef USE_INDICATOR_LED_WHILE_RAMPING -#endif - -#define RAMP_SIZE 150 - -// 3x7135 + FET -// level_calc.py ninth 2 150 7135 1 11.2 450 FET 1 10 4000 -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12,13,14,15,16,17,18,19,21,22,23,25,26,27,29,31,32,34,36,38,40,42,44,46,49,51,54,56,59,62,65,68,71,74,78,81,85,89,93,97,101,106,110,115,120,125,130,136,141,147,153,160,166,173,180,187,195,202,210,219,227,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,10,11,13,14,16,18,19,21,23,25,27,29,31,34,36,38,41,43,46,48,51,54,57,60,63,66,69,72,76,79,83,87,91,95,99,103,107,112,116,121,126,131,136,141,146,152,158,163,169,175,182,188,195,202,209,216,223,231,239,247,255 -#define MAX_1x7135 83 -#define HALFSPEED_LEVEL 13 -#define QUARTERSPEED_LEVEL 6 - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 120 -// 10, 28, 46, 65, [83], 101, 120 -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// safe limit ~30% power -// 10 34 59 [83] 108 -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL 108 -#define SIMPLE_UI_STEPS 5 - -// thermal regulation parameters -#ifdef MIN_THERM_STEPDOWN -#undef MIN_THERM_STEPDOWN // this should be lower, because 3x7135 instead of 1x7135 -#endif -#define MIN_THERM_STEPDOWN 60 // lowest value it'll step down to -#define THERM_FASTER_LEVEL (RAMP_SIZE-20) // don't throttle back faster when high - -// too big, turn off extra features -#undef USE_TACTICAL_MODE diff --git a/spaghetti-monster/anduril/cfg-emisar-d4sv2-219.h b/spaghetti-monster/anduril/cfg-emisar-d4sv2-219.h deleted file mode 100644 index 22775cc..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-d4sv2-219.h +++ /dev/null @@ -1,18 +0,0 @@ -// Emisar D4Sv2-219 config options for Anduril -// Copyright (C) 2019-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#include "cfg-emisar-d4sv2.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0134" -// ATTINY: 1634 - -// don't turn off first channel at turbo level -#undef PWM1_LEVELS -#undef PWM2_LEVELS -#define PWM1_LEVELS 1,1,2,2,3,3,4,5,5,6,7,8,9,10,11,12,13,17,18,19,20,21,22,24,26,28,30,33,35,38,41,44,47,50,54,57,61,65,69,74,79,84,89,94,100,106,113,119,126,134,142,150,158,167,176,186,196,207,218,230,242,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,16,21,25,30,35,41,46,52,58,64,71,77,84,92,99,107,115,124,133,142,151,161,172,182,193,205,217,229,242,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -// 65% FET power -#undef PWM3_LEVELS -#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,4,6,7,8,10,11,12,13,15,17,18,20,21,23,25,27,29,31,33,35,38,39,42,44,47,49,52,54,57,60,63,66,69,73,76,79,83,86,90,94,98,102,106,110,115,119,124,129,134,139,144,149,155,160,166 diff --git a/spaghetti-monster/anduril/cfg-emisar-d4sv2.h b/spaghetti-monster/anduril/cfg-emisar-d4sv2.h deleted file mode 100644 index 666b394..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-d4sv2.h +++ /dev/null @@ -1,81 +0,0 @@ -// Emisar D4S V2 config options for Anduril -// Copyright (C) 2019-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0133" -#include "hwdef-emisar-d4sv2.h" -#include "hank-cfg.h" -// ATTINY: 1634 - -#define RAMP_SIZE 150 - -// 1x7135 + 3x7135 + FET - -// level_calc.py 5.7895 3 150 7135 1 0.1 130 7135 1 1 321.43 FET 2 10 3000 --pwm dyn:74:4096:255:3 -#define PWM1_LEVELS 1,1,2,3,3,4,5,6,7,8,9,11,12,13,15,16,18,19,21,23,26,27,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,71,74,76,78,80,82,85,87,90,93,96,100,103,107,112,116,122,127,133,140,147,154,163,171,182,192,203,215,228,241,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,12,19,25,32,40,47,55,64,72,81,91,101,111,122,133,144,156,169,182,195,209,224,239,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,5,7,10,12,15,17,20,22,25,28,31,34,38,41,44,48,52,56,60,64,68,72,77,81,86,91,96,101,107,112,118,124,130,136,143,149,156,163,170,178,186,193,201,210,218,227,236,245,255 -#define PWM_TOPS 4095,2701,3200,3586,2518,2778,2834,2795,2705,2587,2455,2582,2412,2247,2256,2091,2062,1907,1860,1802,1737,1605,1542,1477,1412,1347,1284,1222,1162,1105,1050,997,946,898,853,810,768,730,693,658,625,594,564,536,503,485,462,439,418,398,384,366,353,340,327,319,307,298,292,284,280,273,269,266,263,260,258,256,256,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 - -#define MIN_THERM_STEPDOWN 70 // should be above highest dyn_pwm level -#define MAX_1x7135 75 -#define MAX_Nx7135 100 -#define HALFSPEED_LEVEL 20 -#define QUARTERSPEED_LEVEL 5 - -// old -//// ../../../bin/level_calc.py seventh 3 150 7135 1 2.3 130 7135 11 5 400.1 FET 2 10 4000 -//// (and some manual edits to make the clock speed changes smoother) -//#define PWM1_LEVELS 1,1,2,2,3,3,4,5,5,6,7,8,9,10,11,12,13,17,18,19,20,21,22,24,26,28,30,33,35,38,41,44,47,50,54,57,61,65,69,74,79,84,89,94,100,106,113,119,126,134,142,150,158,167,176,186,196,207,218,230,242,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -//#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,16,21,25,30,35,41,46,52,58,64,71,77,84,92,99,107,115,124,133,142,151,161,172,182,193,205,217,229,242,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -//#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,5,6,8,10,12,14,16,18,20,23,25,27,30,32,35,38,41,44,47,50,53,57,60,64,67,71,75,79,83,87,92,96,101,106,111,116,121,127,132,138,144,150,156,163,169,176,183,190,197,205,213,221,229,237,246,255 -//#define PWM_TOPS 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -//#define MAX_1x7135 62 -//#define MAX_Nx7135 93 -//#define HALFSPEED_LEVEL 18 -//#define QUARTERSPEED_LEVEL 8 - - -#define DEFAULT_LEVEL MAX_Nx7135 - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 130 -// 20, 38, 56, [75], 93, 111, 130 -#define RAMP_DISCRETE_FLOOR 20 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// safe limit ~35% power, 150% of sustainable thermal power -// 25 50 [75] [100] 125 -#define SIMPLE_UI_FLOOR 25 -#define SIMPLE_UI_CEIL 125 -#define SIMPLE_UI_STEPS 5 - -#define STROBE_BRIGHTNESS MAX_LEVEL - -// stop panicking at ~50% power or ~2000 lm -#define THERM_FASTER_LEVEL 130 - -#define THERM_CAL_OFFSET 5 - -// show each channel while it scroll by in the menu -#define USE_CONFIG_COLORS - -// blink numbers on the main LEDs by default (but allow user to change it) -#define DEFAULT_BLINK_CHANNEL CM_MAIN - -// use aux red + aux blue for police strobe -#define USE_POLICE_COLOR_STROBE_MODE -#define POLICE_STROBE_USES_AUX -#define POLICE_COLOR_STROBE_CH1 CM_AUXRED -#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU - -// the default of 26 looks a bit rough, so increase it to make it smoother -#define CANDLE_AMPLITUDE 33 - -// don't blink while ramping -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h b/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h deleted file mode 100644 index e9775ec..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h +++ /dev/null @@ -1,17 +0,0 @@ -// Emisar D4v2-219 config options for Anduril -// Copyright (C) 2019-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#include "cfg-emisar-d4v2.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0114" -// ATTINY: 1634 - -// don't turn off the low channel at turbo level -#undef LOW_PWM_LEVELS -#define LOW_PWM_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -// 65% DDFET power -#undef HIGH_PWM_LEVELS -#define HIGH_PWM_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,15,16,17,18,19,21,22,23,25,26,27,28,30,32,33,34,36,38,39,41,42,44,46,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,78,80,82,84,87,90,92,94,97,99,102,104,108,110,113,116,119,121,125,127,130,134,136,140,143,146,149,153,156,159,163,166 - diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h b/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h deleted file mode 100644 index 6eddb40..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h +++ /dev/null @@ -1,62 +0,0 @@ -// Emisar D4v2-noFET config options for Anduril -// Copyright (C) 2020-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// switch to 1-channel support functions -#define HWDEF_C_FILE hwdef-emisar-d4v2-nofet.c - -#include "cfg-emisar-d4v2.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0115" -// ATTINY: 1634 - -// the ramp uses only 1x7135 chip, max ~130 lm -#undef PWM_CHANNELS -#define PWM_CHANNELS 1 - -#undef PWM1_LEVELS -#undef PWM2_LEVELS -#undef PWM_TOPS -//#define PWM1_LEVELS 1,1,1,2,2,2,2,3,3,3,3,4,4,5,5,6,6,6,7,8,8,9,9,10,10,11,12,13,13,14,15,16,16,17,18,19,20,21,22,23,23,24,26,27,28,29,30,31,32,33,34,36,37,38,39,41,42,43,45,46,47,49,50,52,53,55,56,58,59,61,62,64,66,67,69,71,72,74,76,78,80,81,83,85,87,89,91,93,95,97,99,101,103,105,107,109,111,113,116,118,120,122,125,127,129,132,134,136,139,141,144,146,148,151,154,156,159,161,164,166,169,172,174,177,180,183,185,188,191,194,197,200,203,205,208,211,214,217,220,223,226,230,233,236,239,242,245,249,252,255 -// level_calc.py 3.01 1 150 7135 -1 0.1 140 --pwm dyn:64:4096:255:3 --clock 11:21:8.0 -// (and some manual tweaks to make half/quarter speed levels less bumpy) -#define PWM1_LEVELS 1,1,2,2,3,4,4,5,6,7,9,10,11,11,12,13,13,14,15,15,18,18,17,18,18,19,19,19,19,19,20,20,20,20,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,21,21,22,23,24,24,26,27,28,29,30,31,32,33,34,35,36,38,39,40,42,43,44,46,47,49,50,52,53,55,56,58,60,62,63,65,67,69,71,73,75,77,79,81,83,85,88,90,92,95,97,100,102,105,107,110,112,115,118,121,124,127,129,132,135,139,142,145,148,151,155,158,162,165,169,172,176,179,183,187,191,195,199,203,207,211,215,219,223,228,232,237,241,246,250,255 -#define PWM_TOPS 4094,2719,3280,1954,2599,3032,2342,2548,2626,2635,2246,2261,2244,1964,1956,1929,1743,1733,1763,1697,1492,1362,1245,1231,1132,1118,1034,958,889,826,821,767,717,671,629,591,556,523,493,465,440,416,394,373,354,336,319,304,299,293,279,282,269,257,260,249,251,252,253,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 - -#undef MAX_1x7135 -#define MAX_1x7135 150 -#undef QUARTERSPEED_LEVEL -#undef HALFSPEED_LEVEL -#define QUARTERSPEED_LEVEL 11 -#define HALFSPEED_LEVEL 21 - -#undef DEFAULT_LEVEL -#define DEFAULT_LEVEL 80 - -#undef RAMP_SMOOTH_CEIL -#define RAMP_SMOOTH_CEIL 150 -// 10, 45, 80, 115, 150 -#undef RAMP_DISCRETE_FLOOR -#undef RAMP_DISCRETE_CEIL -#undef RAMP_DISCRETE_STEPS -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL 150 -#define RAMP_DISCRETE_STEPS 5 - -// safe limit ~100% power because no FET -#undef SIMPLE_UI_FLOOR -#undef SIMPLE_UI_CEIL -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL - -#undef CANDLE_AMPLITUDE -#define CANDLE_AMPLITUDE 60 - -#undef THERM_FASTER_LEVEL -#define THERM_FASTER_LEVEL 150 - -// maybe keep this, in case someone uses a higher power channel? -//#undef USE_THERMAL_REGULATION -//#undef USE_SET_LEVEL_GRADUALLY - diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2.h b/spaghetti-monster/anduril/cfg-emisar-d4v2.h deleted file mode 100644 index 0f0bb17..0000000 --- a/spaghetti-monster/anduril/cfg-emisar-d4v2.h +++ /dev/null @@ -1,64 +0,0 @@ -// Emisar D4v2 config options for Anduril -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0113" -#include "hwdef-emisar-d4v2.h" -#include "hank-cfg.h" -// ATTINY: 1634 - -#define RAMP_SIZE 150 - -// 7135 at 75/150 -// level_calc.py 5.7895 2 150 7135 1 0.1 130 FET 1 10 3000 --pwm dyn:74:4096:255:3 -// (with some manual tweaks) -// non-zero part of FET channel calculated with: -// level_calc.py 3 1 75 7135 2 500 3000 -#define PWM1_LEVELS 1,1,2,3,3,4,5,6,7,8,9,11,12,13,15,16,18,19,21,23,26,27,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,71,74,76,78,80,82,85,87,90,93,96,100,103,107,112,116,122,127,133,140,147,154,163,171,182,192,203,215,228,241,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,5,7,9,11,13,15,17,19,21,23,25,27,29,32,34,36,39,41,43,46,49,51,54,57,59,62,65,68,71,74,77,80,83,86,90,93,96,100,103,107,110,114,117,121,125,129,133,137,141,145,149,153,157,162,166,170,175,179,184,189,193,198,203,208,213,218,223,228,233,239,244,249,255 -#define PWM_TOPS 4095,2701,3200,3586,2518,2778,2834,2795,2705,2587,2455,2582,2412,2247,2256,2091,2062,1907,1860,1802,1737,1605,1542,1477,1412,1347,1284,1222,1162,1105,1050,997,946,898,853,810,768,730,693,658,625,594,564,536,503,485,462,439,418,398,384,366,353,340,327,319,307,298,292,284,280,273,269,266,263,260,258,256,256,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 - -#define MAX_1x7135 75 -#define DEFAULT_LEVEL 50 -#define MIN_THERM_STEPDOWN 70 // should be above highest dyn_pwm level -#define HALFSPEED_LEVEL 20 -#define QUARTERSPEED_LEVEL 5 - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 130 -// 20, 38, 56, [75], 93, 111, 130 -#define RAMP_DISCRETE_FLOOR 20 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// 20, 40, 60, 80, 100 -#define SIMPLE_UI_FLOOR 20 -#define SIMPLE_UI_CEIL 100 -#define SIMPLE_UI_STEPS 5 - -// stop panicking at ~25% power or ~1000 lm -#define THERM_FASTER_LEVEL 110 - -#define THERM_CAL_OFFSET 5 - -// show each channel while it scroll by in the menu -#define USE_CONFIG_COLORS - -// blink numbers on the main LEDs by default (but allow user to change it) -#define DEFAULT_BLINK_CHANNEL CM_MAIN - -// use aux red + aux blue for police strobe -#define USE_POLICE_COLOR_STROBE_MODE -#define POLICE_STROBE_USES_AUX -#define POLICE_COLOR_STROBE_CH1 CM_AUXRED -#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU - -// the default of 26 looks a bit rough, so increase it to make it smoother -#define CANDLE_AMPLITUDE 33 - -// don't blink while ramping -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - diff --git a/spaghetti-monster/anduril/cfg-ff-e01.h b/spaghetti-monster/anduril/cfg-ff-e01.h deleted file mode 100644 index 07bda87..0000000 --- a/spaghetti-monster/anduril/cfg-ff-e01.h +++ /dev/null @@ -1,57 +0,0 @@ -// Fireflies E01 SST-40 thrower config options for Anduril -// Copyright (C) 2019-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0441" -#include "hwdef-ff-pl47.h" -// ATTINY: 85 - -// disable indicator LED; it's hardwired -#ifdef USE_INDICATOR_LED -#undef USE_INDICATOR_LED -#endif - - -#define RAMP_SIZE 150 - -// driver is a FET + 3x7135, ~413 lm at highest regulated level -// ../../../bin/level_calc.py seventh 2 150 7135 1 12 414 FET 2 10 1930 -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12,13,14,15,15,16,17,18,20,21,22,23,24,26,27,28,30,31,33,34,36,38,39,41,43,45,47,49,51,53,56,58,60,63,65,68,71,74,77,80,83,86,89,93,96,100,103,107,111,115,119,124,128,132,137,142,147,152,157,163,168,174,180,186,192,198,204,211,218,225,232,240,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,7,9,12,14,16,19,22,24,27,30,33,36,39,42,45,48,52,55,58,62,66,69,73,77,81,85,90,94,98,103,107,112,117,122,127,132,137,143,148,154,160,166,172,178,184,191,197,204,211,218,225,232,240,247,255 -#define MAX_1x7135 93 -#define HALFSPEED_LEVEL 14 -#define QUARTERSPEED_LEVEL 7 - -// ceiling is level 130/150 (50% power) -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 130 - -// 20, 38, 56, 75, [93], 111, 130 (93 is highest regulated) -// (9 / 45 / 116 / 248 / 467 / 742 / 1280 + 2140 lm) -#define RAMP_DISCRETE_FLOOR 20 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// safe limit ~50% power -// 20 56 [93] 130 -#define SIMPLE_UI_FLOOR 20 -#define SIMPLE_UI_CEIL 130 -#define SIMPLE_UI_STEPS 4 - -// thermal regulation parameters -#define MIN_THERM_STEPDOWN 65 // lowest value it'll step down to -#define THERM_FASTER_LEVEL 130 - -#ifndef BLINK_AT_RAMP_CEIL -#define BLINK_AT_RAMP_CEIL -#endif - -// don't do this -#undef BLINK_AT_RAMP_MIDDLE - -#define USE_SMOOTH_STEPS - -// too big, turn off extra features -#undef USE_SOS_MODE -//#undef USE_TACTICAL_MODE diff --git a/spaghetti-monster/anduril/cfg-ff-pl47-219.h b/spaghetti-monster/anduril/cfg-ff-pl47-219.h deleted file mode 100644 index 11bd5b1..0000000 --- a/spaghetti-monster/anduril/cfg-ff-pl47-219.h +++ /dev/null @@ -1,15 +0,0 @@ -// Fireflies PL47-219B config options for Anduril -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// same as PL47 but with FET modes limited to 67% power -// to avoid destroying the LEDs -#include "cfg-ff-pl47.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0422" - -#undef PWM1_LEVELS -#undef PWM2_LEVELS -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12,13,14,15,16,17,18,19,21,22,23,25,26,27,29,31,32,34,36,38,40,42,44,46,49,51,54,56,59,62,65,68,71,74,78,81,85,89,93,97,101,106,110,115,120,125,130,136,141,147,153,160,166,173,180,187,195,202,210,219,227,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,15,16,17,19,20,22,23,25,27,28,30,31,33,35,37,39,41,43,45,47,50,52,55,57,60,63,65,68,71,74,77,80,83,87,90,93,97,101,105,108,112,116,121,125,129,134,139,143,148,153,159,164,169 diff --git a/spaghetti-monster/anduril/cfg-ff-pl47.h b/spaghetti-monster/anduril/cfg-ff-pl47.h deleted file mode 100644 index 6436477..0000000 --- a/spaghetti-monster/anduril/cfg-ff-pl47.h +++ /dev/null @@ -1,79 +0,0 @@ -// Fireflies PL47 config options for Anduril -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0421" -#include "hwdef-ff-pl47.h" -// ATTINY: 85 - -// the button lights up -#define USE_INDICATOR_LED -// the aux LEDs are in the same place as the main LEDs -#ifdef USE_INDICATOR_LED_WHILE_RAMPING -#undef USE_INDICATOR_LED_WHILE_RAMPING -#endif -//#define USE_INDICATOR_LED_WHILE_RAMPING - -// off mode: high (2) -// lockout: blinking (3) -#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 2) - -// the "low" mode doesn't work on this light's aux LEDs -// (but it does work on the switch LEDs) -// Fireflies wants to skip aux LED mode 1 (low) -#define INDICATOR_LED_SKIP_LOW - - -#define RAMP_SIZE 150 - -// driver is a FET + 3x7135, ~400 lm at highest regulated level -// ramp copied from Emisar D4S ramp -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12,13,14,15,16,17,18,19,21,22,23,25,26,27,29,31,32,34,36,38,40,42,44,46,49,51,54,56,59,62,65,68,71,74,78,81,85,89,93,97,101,106,110,115,120,125,130,136,141,147,153,160,166,173,180,187,195,202,210,219,227,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,10,11,13,14,16,18,19,21,23,25,27,29,31,34,36,38,41,43,46,48,51,54,57,60,63,66,69,72,76,79,83,87,91,95,99,103,107,112,116,121,126,131,136,141,146,152,158,163,169,175,182,188,195,202,209,216,223,231,239,247,255 -#define MAX_1x7135 83 -#define HALFSPEED_LEVEL 13 -#define QUARTERSPEED_LEVEL 6 - -// ceiling is level 120/150 -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 120 - -// 10, 28, 46, 65, 83, 101, 120 (83 is highest regulated) -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL 120 -#define RAMP_DISCRETE_STEPS 7 - -// safe limit ~25% power / ~1000 lm -// 10 34 59 [83] 108 -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL 108 -#define SIMPLE_UI_STEPS 5 - -// ~25 lm to ~300 lm -//#define SIMPLE_UI_FLOOR 30 -//#define SIMPLE_UI_CEIL MAX_1x7135 -// ~50 lm to ~500 lm -//#define SIMPLE_UI_FLOOR 40 -//#define SIMPLE_UI_CEIL 90 - -// thermal regulation parameters -#ifdef MIN_THERM_STEPDOWN -#undef MIN_THERM_STEPDOWN // this should be lower, because 3x7135 instead of 1x7135 -#endif -#define MIN_THERM_STEPDOWN 60 // lowest value it'll step down to -// regulate down faster when the FET is active, slower otherwise -#define THERM_FASTER_LEVEL 135 // throttle back faster when high - -#ifndef BLINK_AT_RAMP_CEIL -#define BLINK_AT_RAMP_CEIL -#endif - -// don't do this -#undef BLINK_AT_RAMP_MIDDLE - -#define USE_SMOOTH_STEPS - -// too big, turn off extra features -#undef USE_SOS_MODE -#undef USE_TACTICAL_MODE diff --git a/spaghetti-monster/anduril/cfg-ff-pl47g2.h b/spaghetti-monster/anduril/cfg-ff-pl47g2.h deleted file mode 100644 index 830d7e8..0000000 --- a/spaghetti-monster/anduril/cfg-ff-pl47g2.h +++ /dev/null @@ -1,67 +0,0 @@ -// Fireflies PL47 G2 config options for Anduril -// Copyright (C) 2019-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0423" -#include "hwdef-ff-pl47.h" -// ATTINY: 85 - -// the button lights up -#define USE_INDICATOR_LED -// the aux LEDs are in the same place as the main LEDs -#ifdef USE_INDICATOR_LED_WHILE_RAMPING -#undef USE_INDICATOR_LED_WHILE_RAMPING -#endif - -// off mode: low (1) -// lockout: blinking (3) -#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) - - - -#define RAMP_SIZE 150 - -// driver is a FET + 3x7135, ~400 lm at highest regulated level -// ramp copied from Emisar D4S ramp -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12,13,14,15,16,17,18,19,21,22,23,25,26,27,29,31,32,34,36,38,40,42,44,46,49,51,54,56,59,62,65,68,71,74,78,81,85,89,93,97,101,106,110,115,120,125,130,136,141,147,153,160,166,173,180,187,195,202,210,219,227,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,10,11,13,14,16,18,19,21,23,25,27,29,31,34,36,38,41,43,46,48,51,54,57,60,63,66,69,72,76,79,83,87,91,95,99,103,107,112,116,121,126,131,136,141,146,152,158,163,169,175,182,188,195,202,209,216,223,231,239,247,255 -#define MAX_1x7135 83 -#define HALFSPEED_LEVEL 13 -#define QUARTERSPEED_LEVEL 6 - -// ceiling is level 120/150 -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 120 - -// 10, 28, 46, 65, 83, 101, 120 (83 is highest regulated) -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL 120 -#define RAMP_DISCRETE_STEPS 7 - -// safe limit ~25% power / ~1000 lm -// 10 34 59 [83] 108 -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL 108 -#define SIMPLE_UI_STEPS 5 - -// thermal regulation parameters -#ifdef MIN_THERM_STEPDOWN -#undef MIN_THERM_STEPDOWN // this should be lower, because 3x7135 instead of 1x7135 -#endif -#define MIN_THERM_STEPDOWN 60 // lowest value it'll step down to -// regulate down faster when the FET is active, slower otherwise -#define THERM_FASTER_LEVEL 135 // throttle back faster when high - -#ifndef BLINK_AT_RAMP_CEIL -#define BLINK_AT_RAMP_CEIL -#endif - -// don't do this -#undef BLINK_AT_RAMP_MIDDLE - -#define USE_SMOOTH_STEPS - -// too big, turn off extra features -#undef USE_SOS_MODE -#undef USE_TACTICAL_MODE diff --git a/spaghetti-monster/anduril/cfg-ff-rot66-219.h b/spaghetti-monster/anduril/cfg-ff-rot66-219.h deleted file mode 100644 index 4dad4e2..0000000 --- a/spaghetti-monster/anduril/cfg-ff-rot66-219.h +++ /dev/null @@ -1,26 +0,0 @@ -// Fireflies ROT66-219 (7x7135) config options for Anduril -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// same as regular ROT66, but calibrated for Nichia 219B with 7x7135 chips -#include "cfg-ff-rot66.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0412" - -// driver is a FET+N+1, -// where N=6 for the 219b version -// calculated to get transition points at level 65 and 115 -// first 65 steps same as regular ROT66 -// remaining steps from this command: -// level_calc.py ninth 3 150 7135 1 1.4 110.1 7135 14 1 1058.5 FET 1 10 4500 -#undef PWM2_LEVELS -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,17,18,20,21,23,25,27,29,31,33,36,38,41,43,46,49,52,55,58,62,65,69,73,77,81,86,90,95,100,105,111,116,122,128,135,141,148,155,163,170,178,187,195,204,214,224,234,244,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#undef PWM3_LEVELS -#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,8,12,16,21,25,30,35,40,45,50,56,62,68,74,81,87,94,101,109,116,124,133,141,150,159,168,178,188,198,209,220,231,243,255 -#undef MAX_Nx7135 -#define MAX_Nx7135 115 - -// regulate down faster when the FET is active, slower otherwise -#undef THERM_FASTER_LEVEL -#define THERM_FASTER_LEVEL 130 // throttle back faster when high diff --git a/spaghetti-monster/anduril/cfg-ff-rot66.h b/spaghetti-monster/anduril/cfg-ff-rot66.h deleted file mode 100644 index 752ddbb..0000000 --- a/spaghetti-monster/anduril/cfg-ff-rot66.h +++ /dev/null @@ -1,65 +0,0 @@ -// Fireflies ROT66 (14x7135) config options for Anduril -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0411" -#include "hwdef-ff-rot66.h" - -// the button lights up -#define USE_INDICATOR_LED -// the button is visible while main LEDs are on -#define USE_INDICATOR_LED_WHILE_RAMPING - -// off mode: high (2) -// lockout: blinking (3) -#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 2) - -#define RAMP_SIZE 150 - -// driver is a FET+N+1, -// where N=6 for the 219b version, -// or N=13 for the XP-L HI version (this version) -// calculated to get transition points at level 65 and 125 -// first 65 steps copied from FW3A -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -// remaining steps from this command: -// level_calc.py ninth 3 150 7135 1 1.4 110.1 7135 15 1 1644.16 FET 1 10 4500 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,17,18,19,20,21,22,23,25,26,27,29,30,32,34,35,37,39,41,43,46,48,50,53,55,58,61,64,67,70,73,77,81,84,88,92,97,101,106,110,115,120,126,131,137,143,149,156,162,169,177,184,192,200,208,217,226,235,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,13,21,28,35,43,51,60,68,77,87,96,106,117,127,138,150,161,173,186,199,212,226,240,255 -#define MAX_1x7135 65 -#define MAX_Nx7135 125 -#define HALFSPEED_LEVEL 14 -#define QUARTERSPEED_LEVEL 8 - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL MAX_Nx7135 -// safe limit max regulated power -// 20 46 72 98 [125] -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL MAX_Nx7135 -#define SIMPLE_UI_STEPS 5 - -// regulate down faster when the FET is active, slower otherwise -#define THERM_FASTER_LEVEL 130 // throttle back faster when high - -// don't do this -#undef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_CEIL - -// enable extra features -#define USE_SMOOTH_STEPS - -// too big, turn off extra features -#undef USE_MOMENTARY_MODE -#undef USE_TACTICAL_MODE -#undef USE_SOS_MODE -//#undef USE_RAMP_AFTER_MOON_CONFIG -//#undef USE_RAMP_SPEED_CONFIG -//#undef USE_VOLTAGE_CORRECTION -//#undef USE_2C_STYLE_CONFIG -//#undef USE_TACTICAL_STROBE_MODE -//#ifdef USE_LOWPASS_WHILE_ASLEEP -//#undef USE_LOWPASS_WHILE_ASLEEP -//#endif - diff --git a/spaghetti-monster/anduril/cfg-ff-rot66g2.h b/spaghetti-monster/anduril/cfg-ff-rot66g2.h deleted file mode 100644 index 04aac53..0000000 --- a/spaghetti-monster/anduril/cfg-ff-rot66g2.h +++ /dev/null @@ -1,59 +0,0 @@ -// Fireflies ROT66 G2 config options for Anduril -// Copyright (C) 2019-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#include "cfg-ff-rot66.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0413" - -// if the "low" mode was disabled, turn it back on -#ifdef INDICATOR_LED_SKIP_LOW -#undef INDICATOR_LED_SKIP_LOW -#endif - -// lockout: blinking (3), off: low (1) -#ifdef INDICATOR_LED_DEFAULT_MODE -#undef INDICATOR_LED_DEFAULT_MODE -#endif -#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) - -// the button is *not* visible while main LEDs are on -#undef USE_INDICATOR_LED_WHILE_RAMPING - -// ramp shape is different than original ROT66 -// 1x7135: 150 lm -// Nx7135: 1200 lm -// FET: 4500 lm -// ../../../bin/level_calc.py 7.0 3 150 7135 1 4 180.16 7135 8 1 1374.48 FET 1 10 4000 -// (plus some manual tweaks) -#undef PWM1_LEVELS -#undef PWM2_LEVELS -#undef PWM3_LEVELS -#define PWM1_LEVELS 1,1,2,3,3,4,4,5,6,7,7,8,9,10,11,12,16,17,18,19,20,21,22,24,26,28,30,32,35,37,40,42,45,48,51,55,58,62,65,69,74,78,83,87,92,98,103,109,115,121,128,134,142,149,157,165,173,182,191,201,211,221,232,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,11,13,14,16,18,20,22,24,26,28,30,33,35,38,41,43,46,49,52,55,59,62,66,69,73,77,81,85,89,94,98,103,108,113,118,124,129,135,141,147,153,160,166,173,180,188,195,203,211,219,228,237,246,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,11,17,23,29,36,42,49,56,63,71,78,86,94,102,111,119,128,137,147,156,166,176,187,197,208,219,231,243,255 - -#undef MAX_1x7135 -#undef MAX_Nx7135 -#define MAX_1x7135 65 // ~150 lm -#define MAX_Nx7135 120 // ~1200 m -#undef HALFSPEED_LEVEL -#undef QUARTERSPEED_LEVEL -#define HALFSPEED_LEVEL 17 -#define QUARTERSPEED_LEVEL 6 - -// higher floor than default, and stop at highest regulated level -#define RAMP_SMOOTH_FLOOR 1 // ~0.3 lm -#define RAMP_SMOOTH_CEIL MAX_Nx7135 // ~1200 lm -// 10, 28, 46, [65], 83, 101, [120] -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL MAX_Nx7135 -#define RAMP_DISCRETE_STEPS 7 - -// safe limit max regulated power -// 10 37 65 92 [120] -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL MAX_Nx7135 -#define SIMPLE_UI_STEPS 5 - diff --git a/spaghetti-monster/anduril/cfg-fw3a-219.h b/spaghetti-monster/anduril/cfg-fw3a-219.h deleted file mode 100644 index 0bd5250..0000000 --- a/spaghetti-monster/anduril/cfg-fw3a-219.h +++ /dev/null @@ -1,16 +0,0 @@ -// FW3A-219 config options for Anduril -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#include "cfg-fw3a.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0312" - -#undef PWM1_LEVELS -#undef PWM2_LEVELS -#undef PWM3_LEVELS -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,8,10,13,15,17,19,22,24,26,29,31,34,37,39,42,45,48,51,54,57,60,64,67,70,74,77,81,85,88,92,96,100,104,108,112,116,121,125,130,134,139,143,148,153,158,163,168,173,179,184,189,195,201,206,212,218,224,230,236,243,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,13,19,25,30,36,42,48,54,61,67,73,80,86,93,100,107,114,120,128 - diff --git a/spaghetti-monster/anduril/cfg-fw3a-nofet.h b/spaghetti-monster/anduril/cfg-fw3a-nofet.h deleted file mode 100644 index b5d4166..0000000 --- a/spaghetti-monster/anduril/cfg-fw3a-nofet.h +++ /dev/null @@ -1,54 +0,0 @@ -// FW3A with the FET disabled -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#include "cfg-fw3a.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0313" - -// don't use channel 3 (FET) -#undef PWM_CHANNELS -#undef CH3_PIN -#undef CH3_PWM -#define PWM_CHANNELS 2 - -// reconfigure the ramp -#undef PWM1_LEVELS -#undef PWM2_LEVELS -#undef PWM3_LEVELS -// copied from Emisar D4, mostly -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255 -#undef MAX_1x7135 -#define MAX_1x7135 65 -#undef MAX_Nx7135 -#undef HALFSPEED_LEVEL -#define HALFSPEED_LEVEL 15 -#undef QUARTERSPEED_LEVEL -#define QUARTERSPEED_LEVEL 6 - -#undef RAMP_SMOOTH_FLOOR -#undef RAMP_SMOOTH_CEIL -#undef RAMP_DISCRETE_FLOOR -#undef RAMP_DISCRETE_CEIL -#undef RAMP_DISCRETE_STEPS - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 150 -// 10, 33, 56, 80, 103, 126, 150 -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// safe limit ~25% power -// 10 37 65 92 120 -#undef SIMPLE_UI_CEIL -#define SIMPLE_UI_CEIL 120 - -#undef THERM_FASTER_LEVEL -#define THERM_FASTER_LEVEL 130 - -// without the 3rd channel, extra features can fit -#define USE_TACTICAL_MODE - diff --git a/spaghetti-monster/anduril/cfg-fw3a.h b/spaghetti-monster/anduril/cfg-fw3a.h deleted file mode 100644 index 53c4135..0000000 --- a/spaghetti-monster/anduril/cfg-fw3a.h +++ /dev/null @@ -1,54 +0,0 @@ -// FW3A config options for Anduril -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0311" -#include "hwdef-fw3a.h" - -#define RAMP_SIZE 150 - -// ../../bin/level_calc.py 1 65 7135 1 0.8 150 -// ... mixed with this: -// ../../../bin/level_calc.py 3 150 7135 1 0.33 150 7135 1 1 850 FET 1 10 1500 -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,8,10,13,15,17,19,22,24,26,29,31,34,37,39,42,45,48,51,54,57,60,64,67,70,74,77,81,85,88,92,96,100,104,108,112,116,121,125,130,134,139,143,148,153,158,163,168,173,179,184,189,195,201,206,212,218,224,230,236,243,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,19,31,43,55,67,79,91,104,117,130,143,157,170,184,198,212,226,240,255 - -#define DEFAULT_LEVEL 50 -#define MAX_1x7135 65 -#define MAX_Nx7135 130 -#define HALFSPEED_LEVEL 15 -#define QUARTERSPEED_LEVEL 6 - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL MAX_Nx7135 -// 10, 30, 50, [70], 90, 110, 130 -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// safe limit ~20% power -// 20 40 60 80 100 -#define SIMPLE_UI_FLOOR 20 -#define SIMPLE_UI_CEIL 100 -#define SIMPLE_UI_STEPS 5 - -// stop panicking at about 3A or ~1100 lm, this light is a hotrod -#define THERM_FASTER_LEVEL MAX_Nx7135 - -// don't blink during ramp -#define BLINK_AT_RAMP_CEIL -#undef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_FLOOR - -// enable extra features -#define USE_SMOOTH_STEPS - -// can't reset the normal way because power is connected before the button -#define USE_SOFT_FACTORY_RESET - -// too big, turn off extra features -#undef USE_TACTICAL_MODE -#undef USE_SOS_MODE - diff --git a/spaghetti-monster/anduril/cfg-fw3x-lume1.h b/spaghetti-monster/anduril/cfg-fw3x-lume1.h deleted file mode 100644 index 184ab8e..0000000 --- a/spaghetti-monster/anduril/cfg-fw3x-lume1.h +++ /dev/null @@ -1,81 +0,0 @@ -// lume1 for FW3x config options for Anduril -// Copyright (C) 2020-2023 LoneOceans, Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -/* - * Constant current Buck-Boost + FET driver - * For more information: www.loneoceans.com/labs/ - * Datasheets: - * - 1634: http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8303-8-bit-AVR-Microcontroller-tinyAVR-ATtiny1634_Datasheet.pdf - * - 85: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf - */ - -#define MODEL_NUMBER "0314" -#include "hwdef-fw3x-lume1.h" -// ATTINY: 1634 - -// set this light for 50C thermal ceiling -#undef DEFAULT_THERM_CEIL -#define DEFAULT_THERM_CEIL 50 - -// this light has three aux LED channels: R, G, B -#define USE_AUX_RGB_LEDS - -// it has no independent LED in the button unlike emisar d4 -//#define USE_BUTTON_LED - -// the aux LEDs are front-facing, so turn them off while main LEDs are on -#ifdef USE_INDICATOR_LED_WHILE_RAMPING -#undef USE_INDICATOR_LED_WHILE_RAMPING -#endif - -// ../../bin/level_calc.py cube 1 149 7135 0 0.5 1000, with 0 appended to the end. -// for FET PWM (PWM2), all values are 0, except for last value of 1023 -// (with max_pwm set to 1023) -#define RAMP_SIZE 150 -//#define PWM1_LEVELS 0,0,0,0,1,1,1,1,2,2,2,3,3,4,4,5,5,6,7,7,8,9,10,11,12,13,14,15,16,17,19,20,22,23,25,26,28,30,32,34,36,38,40,42,45,47,49,52,55,58,60,63,66,70,73,76,80,83,87,91,94,98,102,107,111,115,120,124,129,134,139,144,150,155,160,166,172,178,184,190,196,203,209,216,223,230,237,244,252,259,267,275,283,291,299,308,316,325,334,343,353,362,372,382,392,402,412,423,433,444,455,466,478,489,501,513,525,538,550,563,576,589,602,616,630,644,658,672,687,701,716,731,747,762,778,794,810,827,844,861,878,895,913,930,948,967,985,1004,1023,0 -#define PWM1_LEVELS 1,1,1,1,2,2,2,2,3,3,3,4,4,5,5,6,6,7,8,8,9,10,11,12,13,14,15,16,17,18,20,21,23,24,26,27,29,31,33,35,37,39,41,43,45,48,50,53,56,58,61,64,67,70,74,77,80,84,88,91,95,99,103,108,112,116,121,125,130,135,140,145,150,156,161,167,173,178,184,191,197,203,210,217,223,230,238,245,252,260,268,275,283,292,300,308,317,326,335,344,353,363,372,382,392,402,413,423,434,445,456,467,478,490,502,514,526,538,551,563,576,589,603,616,630,644,658,672,687,702,717,732,747,763,778,794,811,827,844,861,878,895,913,931,949,967,985,1004,1023,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1023 -#define DEFAULT_LEVEL 56 -#define MAX_1x7135 149 -// TODO: test if underclocking works on lume1 -//#define HALFSPEED_LEVEL 14 -//#define QUARTERSPEED_LEVEL 5 -// don't slow down at low levels; this isn't that sort of light -// (it needs to stay at full speed for the 10-bit PWM to work) -#ifdef USE_DYNAMIC_UNDERCLOCKING -#undef USE_DYNAMIC_UNDERCLOCKING -#endif - -// the entire ramp is regulated except turbo; don't blink halfway up -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 149 -// turn on BuckBoost from level 1 to 149, but not 150 -// Level 150 is when BuckBoost is off and FET is ON 100% -#define LED_ENABLE_PIN_LEVEL_MIN 1 -#define LED_ENABLE_PIN_LEVEL_MAX 149 -// 10 33 56 79 102 125 [149] -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL 120 -#define SIMPLE_UI_STEPS 5 - -// slow down party strobe; this driver can't pulse for too short a time -#define PARTY_STROBE_ONTIME 4 - -// stop panicking at ~85% regulated power or ~750 lm -#define THERM_FASTER_LEVEL 140 - -#define THERM_CAL_OFFSET 0 // not needed due to external sensor - -// can't reset the normal way because power is connected before the button -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/cfg-gchart-fet1-t1616.h b/spaghetti-monster/anduril/cfg-gchart-fet1-t1616.h deleted file mode 100644 index 4082fca..0000000 --- a/spaghetti-monster/anduril/cfg-gchart-fet1-t1616.h +++ /dev/null @@ -1,63 +0,0 @@ -// gChart's custom FET+1 driver config options for Anduril -// Copyright (C) 2020-2023 gchart, Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "1618" // Golden Ratio... because I can -#include "hwdef-gchart-fet1-t1616.h" -// ATTINY: 1616 - -// the button lights up -#define USE_INDICATOR_LED -// the button is visible while main LEDs are on -#define USE_INDICATOR_LED_WHILE_RAMPING -// off mode: low (1) -// lockout: blinking (3) -#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) - -#define RAMP_SIZE 150 - -// 7135 at 90/150 -// level_calc.py 5.7895 2 150 7135 0 0.1 125.25 FET 1 10 1200 --pwm dyn:61:4096:255:2.5 --clock 5:11:2.0 -// (with heavy manual tweaks up to ~15/150) -#define PWM1_LEVELS 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 23, 24, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 35, 36, 37, 38, 40, 41, 43, 45, 47, 50, 53, 56, 60, 63, 67, 71, 75, 79, 84, 89, 94, 99,104,110,116,122,129,136,143,150,158,166,174,183,192,202,211,222,232,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 5, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 29, 31, 34, 37, 39, 42, 45, 48, 51, 54, 58, 61, 65, 68, 72, 76, 80, 84, 88, 93, 97,102,107,112,117,122,127,133,139,145,151,157,163,170,177,183,191,198,205,213,221,229,238,246,255 -#define PWM_TOPS 4095,2893,3917,2806,3252,2703,2684,2660,2640,2370,3000,2900,2630,2549,2246,2193,2030,1961,1889,1716,1642,1569,1497,1428,1290,1232,1176,1122,1070,976,932,890,849,779,745,685,656,605,579,536,514,476,457,424,407,379,364,340,327,314,302,291,280,276,266,262,257,253,253,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#define MAX_1x7135 90 -#define MIN_THERM_STEPDOWN 60 -#define HALFSPEED_LEVEL 11 -#define QUARTERSPEED_LEVEL 5 -#define DEFAULT_LEVEL 50 - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 130 -// 10 30 50 70 [90] 110 130 -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL 130 -#define RAMP_DISCRETE_STEPS 7 - -// 10 30 50 70 [90] 110 130 -#define SIMPLE_UI_FLOOR 10 -#define SIMPLE_UI_CEIL 130 -#define SIMPLE_UI_STEPS 7 - -// stop panicking at ~50% power -#define THERM_FASTER_LEVEL 130 // throttle back faster when high - -// show each channel while it scroll by in the menu -#define USE_CONFIG_COLORS - -// blink numbers on the aux LEDs by default -//#define DEFAULT_BLINK_CHANNEL CM_AUX - -// the default of 26 looks a bit rough, so increase it to make it smoother -#define CANDLE_AMPLITUDE 40 - -// don't blink mid-ramp -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - -// enable factory reset on 13H without loosening tailcap (required) -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h b/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h deleted file mode 100644 index fb62ac6..0000000 --- a/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h +++ /dev/null @@ -1,74 +0,0 @@ -// Mateminco/Astrolux MF01-Mini options for Anduril -// Copyright (C) 2019-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0521" -#include "hwdef-mateminco-mf01-mini.h" - -// the button lights up -#define USE_INDICATOR_LED -//#define INDICATOR_LED_SKIP_LOW // low mode doesn't work on this driver -// the button is visible while main LEDs are on -//#define USE_INDICATOR_LED_WHILE_RAMPING -// off mode: low (1) -// lockout: blinking (3) -#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) - - -#define RAMP_SIZE 150 - -// measured brightness with Sofirn 5500mAh cell at 3.97V: -// moon: 0.3 lm -// channel 1: 113 lm -// channel 2: 718 lm -// channel 3: 3500 lm -// ../../../bin/level_calc.py ninth 3 150 7135 1 2.5 115.65 7135 11 5 708.65 FET 1 10 3500 -// (plus some manual tweaks for a smoother ramp) -#define PWM1_LEVELS 1,1,2,2,3,3,4,5,5,6,7,8,9,9,10,14,15,16,17,18,19,20,21,22,24,26,28,30,32,34,37,39,42,45,48,51,54,58,62,65,69,74,78,83,88,93,98,104,110,116,123,130,137,145,153,161,170,179,188,198,208,219,231,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,13,16,18,20,23,25,28,31,34,37,40,43,47,50,54,58,62,66,70,75,80,85,90,95,100,106,112,118,125,131,138,145,153,161,169,177,185,194,204,213,223,233,244,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,6,10,13,17,21,24,28,33,37,41,46,50,55,60,66,71,76,82,88,94,101,107,114,121,128,135,143,151,159,167,176,185,194,203,213,223,233,244,255 - -#define MAX_1x7135 65 // ~113 lm -#define MAX_Nx7135 110 -#define HALFSPEED_LEVEL 16 -#define QUARTERSPEED_LEVEL 8 - -#define RAMP_SMOOTH_FLOOR 1 // ~0.3 lm -#define RAMP_SMOOTH_CEIL 130 // ~??? lm -// 14/135/6 = 14, 38, 62, 86, [110], 135 -// 20/110/7 = 20, 35, 50, [65], 80, 95, [110] -// 15/130/7 = 15, 34, 53, 72, 91, [110], 130 <-- -#define RAMP_DISCRETE_FLOOR 15 // ~?? lm -#define RAMP_DISCRETE_CEIL 130 // ~??? lm -#define RAMP_DISCRETE_STEPS 7 // ??, ??, ... lm - -// safe limit max regulated power -// 15 38 62 86 [110] -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL 110 -#define SIMPLE_UI_STEPS 5 - - -#define THERM_FASTER_LEVEL 130 // throttle back faster when high - -// don't blink during ramp -//#define BLINK_AT_RAMP_CEIL -#undef BLINK_AT_RAMP_CEIL -#undef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_FLOOR - -// enable extra features -#define USE_SMOOTH_STEPS - -// too big, turn off extra features -#define USE_SOFT_FACTORY_RESET -//#undef USE_SIMPLE_UI -#undef USE_TACTICAL_MODE -#undef USE_SOS_MODE -#undef USE_BEACON_MODE -#undef USE_RAMP_AFTER_MOON_CONFIG -//#undef USE_RAMP_SPEED_CONFIG -#undef USE_VOLTAGE_CORRECTION -//#undef USE_2C_STYLE_CONFIG - diff --git a/spaghetti-monster/anduril/cfg-mateminco-mf01s.h b/spaghetti-monster/anduril/cfg-mateminco-mf01s.h deleted file mode 100644 index de92693..0000000 --- a/spaghetti-monster/anduril/cfg-mateminco-mf01s.h +++ /dev/null @@ -1,61 +0,0 @@ -// Mateminco MT18S / Astrolux MF01S options for Anduril -// Copyright (C) 2019-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0511" -#include "hwdef-mateminco-mf01s.h" -// ATTINY: 85 - -// the button lights up -#define USE_INDICATOR_LED -// the button is visible while main LEDs are on -//#define USE_INDICATOR_LED_WHILE_RAMPING -// off mode: low (1) -// lockout: blinking (3) -#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) - - -#define RAMP_SIZE 150 - -// measured brightness with 4x30Q cells at 4.11V: -// moon: 2.5 lm -// channel 1: 617 lm -// channel 2: 13500 lm -// ../../../bin/level_calc.py seventh 2 150 7135 1 12 717 FET 1 10 13000 -// (with some manual tweaks afterward) -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,7,7,8,9,10,11,12,13,14,15,17,18,19,21,22,24,26,28,30,32,34,36,38,41,44,46,49,52,55,59,62,66,70,74,78,83,87,92,97,102,108,114,120,126,133,139,147,154,162,170,178,187,196,206,215,226,236,248,255,235,255,240,255,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,3,3,4,4,5,6,7,8,9,10,11,12,13,14,16,17,19,20,22,23,25,26,28,30,31,33,35,37,39,41,43,45,47,49,52,54,57,59,62,65,67,70,73,76,80,83,86,90,93,97,101,105,109,113,117,122,126,131,135,140,145,151,156,161,167,173,179,185,191,197,204,211,218,225,232,239,247,255 - -#define MAX_1x7135 70 // ~626 lm -#define HALFSPEED_LEVEL 23 -#define QUARTERSPEED_LEVEL 6 - -#define RAMP_SMOOTH_FLOOR 1 // ~2.5 lm -#define RAMP_SMOOTH_CEIL 120 // ~5400 lm -// 20, 36, 53, [70], 86, 103, 120 -#define RAMP_DISCRETE_FLOOR 20 // 35 lm -#define RAMP_DISCRETE_CEIL 120 // ~5400 lm -#define RAMP_DISCRETE_STEPS 7 // 35, 108, 280, 626, 1500, 2930, 5400 lm - -// safe limit ~25% power -// 18 35 52 [70] 87 105 -#define SIMPLE_UI_FLOOR 18 -#define SIMPLE_UI_CEIL 105 -#define SIMPLE_UI_STEPS 6 - -#define THERM_FASTER_LEVEL 125 // throttle back faster when high (>6000 lm) -#define THERM_HARD_TURBO_DROP // this light is massively overpowered - -// don't blink during ramp, it's irrelevant and annoying on this light -#define BLINK_AT_RAMP_CEIL -#undef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_FLOOR - -// enable extra features -#define USE_SMOOTH_STEPS - -// too big, turn off extra features -#undef USE_TACTICAL_MODE -#undef USE_SOS_MODE - diff --git a/spaghetti-monster/anduril/cfg-mateminco-mt35-mini.h b/spaghetti-monster/anduril/cfg-mateminco-mt35-mini.h deleted file mode 100644 index 1ca5008..0000000 --- a/spaghetti-monster/anduril/cfg-mateminco-mt35-mini.h +++ /dev/null @@ -1,65 +0,0 @@ -// Mateminco MT35 Mini / Astrolux FT03 -// Copyright (C) 2022-2023 (original author TBD), Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0531" -#include "hwdef-mateminco-mt35-mini.h" -// ATTINY: 85 - -// the button lights up -#define USE_INDICATOR_LED -// the button is visible while main LEDs are on -#define USE_INDICATOR_LED_WHILE_RAMPING - -#define RAMP_SIZE 150 - -// level_calc.py fifth 2 150 7135 1 1 120 FET 1 10 2000 -#define PWM1_LEVELS 1,2,2,3,4,5,6,7,8,9,11,13,14,16,18,21,23,26,28,31,35,38,41,45,49,53,58,63,67,73,78,84,90,96,102,109,116,124,131,139,147,156,165,174,184,194,204,215,226,237,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,3,4,5,6,7,8,9,10,11,12,14,15,16,17,18,20,21,22,24,25,27,28,30,31,33,35,36,38,40,42,43,45,47,49,51,53,55,57,59,62,64,66,68,71,73,75,78,80,83,86,88,91,94,96,99,102,105,108,111,114,117,120,124,127,130,134,137,140,144,148,151,155,159,162,166,170,174,178,182,186,190,195,199,203,208,212,217,221,226,231,235,240,245,250,255 - -#define DEFAULT_LEVEL 46 -#define MAX_1x7135 52 -#define HALFSPEED_LEVEL 20 -#define QUARTERSPEED_LEVEL 10 - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 112 -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// safe limit ~33% power -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL -#define SIMPLE_UI_STEPS 5 - -// this light should be fine running a bit hotter than most -#undef DEFAULT_THERM_CEIL -#define DEFAULT_THERM_CEIL 55 - -// stop panicking at ~40% power -#define THERM_FASTER_LEVEL 120 -#define MIN_THERM_STEPDOWN 95 // ~600 lumens -#define THERM_RESPONSE_MAGNITUDE 16 // smaller adjustments, big body. default = 64 - -#define DEFAULT_2C_STYLE 1 // enable 2 click turbo (Anduril 1 style) - -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - -// Allow 3C in Simple UI for switching between smooth and stepped ramping -#define USE_SIMPLE_UI_RAMPING_TOGGLE - -// enable extra features -#define USE_SMOOTH_STEPS - -// too big, turn off extra features -#undef USE_TACTICAL_MODE -#undef USE_SOS_MODE -//#undef USE_RAMP_AFTER_MOON_CONFIG -//#undef USE_RAMP_SPEED_CONFIG -//#undef USE_VOLTAGE_CORRECTION -//#undef USE_2C_STYLE_CONFIG - diff --git a/spaghetti-monster/anduril/cfg-noctigon-dm11-boost.h b/spaghetti-monster/anduril/cfg-noctigon-dm11-boost.h deleted file mode 100644 index 2174fdb..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-dm11-boost.h +++ /dev/null @@ -1,91 +0,0 @@ -// Noctigon DM11 (boost driver) config options for Anduril -// Copyright (C) 2021-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0273" -#include "hwdef-noctigon-dm11-boost.h" -#include "hank-cfg.h" -// ATTINY: 1634 - -#define RAMP_SIZE 150 - -// power channels: -// - boost: 8A? -// - DD FET: none (can't do DD on a boost driver) - -#if 0 // old, dynamic PWM method -// level_calc.py 5.01 1 149 7135 1 0.3 1740 --pwm dyn:78:16384:255 -// (plus a 0 at the beginning for moon) -#define PWM1_LEVELS 0,1,1,1,2,3,3,4,5,6,7,8,9,10,11,13,14,16,17,19,21,23,25,27,29,31,34,36,39,42,44,47,50,53,57,60,63,67,70,74,77,81,85,88,92,96,99,103,107,110,113,117,120,123,126,128,130,133,134,136,137,137,137,137,136,135,133,130,126,122,117,111,104,96,87,76,65,52,38,22,23,25,26,27,28,29,30,32,33,34,36,37,39,40,42,43,45,47,49,51,53,55,57,59,61,63,66,68,70,73,76,78,81,84,87,90,93,96,99,103,106,110,113,117,121,125,129,133,137,142,146,151,155,160,165,170,175,181,186,192,197,203,209,215,222,228,234,241,248,255 -#define PWM_TOPS 16383,16383,12404,8140,11462,14700,11041,12947,13795,14111,14124,13946,13641,13248,12791,13418,12808,13057,12385,12428,12358,12209,12000,11746,11459,11147,11158,10793,10708,10576,10173,9998,9800,9585,9527,9278,9023,8901,8634,8486,8216,8053,7881,7615,7440,7261,7009,6832,6656,6422,6196,6031,5819,5615,5419,5190,4973,4803,4571,4386,4179,3955,3745,3549,3340,3145,2940,2729,2513,2312,2109,1903,1697,1491,1286,1070,871,662,459,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#define MAX_1x7135 150 -#define DEFAULT_LEVEL 70 -#define MIN_THERM_STEPDOWN 80 // must be > end of dynamic PWM range -#define HALFSPEED_LEVEL 12 -#define QUARTERSPEED_LEVEL 4 -#endif - -// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits) -// level_calc.py 5.01 1 150 7135 0 0.2 2000 --pwm 32640 -// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) -#define PWM1_LEVELS 0,1,2,3,4,5,6,7,9,10,12,14,17,19,22,25,28,32,36,41,45,50,56,62,69,76,84,92,101,110,121,132,143,156,169,184,199,215,232,251,270,291,313,336,360,386,414,442,473,505,539,574,612,651,693,736,782,829,880,932,987,1045,1105,1168,1233,1302,1374,1449,1527,1608,1693,1781,1873,1969,2068,2172,2279,2391,2507,2628,2753,2883,3018,3158,3303,3454,3609,3771,3938,4111,4289,4475,4666,4864,5068,5280,5498,5724,5957,6197,6445,6701,6965,7237,7518,7808,8106,8413,8730,9056,9392,9737,10093,10459,10835,11223,11621,12031,12452,12884,13329,13786,14255,14737,15232,15741,16262,16798,17347,17911,18489,19082,19691,20314,20954,21609,22281,22969,23674,24397,25137,25895,26671,27465,28279,29111,29963,30835,31727,32640 -#define MIN_THERM_STEPDOWN 50 -#define DEFAULT_LEVEL 70 -#define MAX_1x7135 150 -// always run at 1/4th speed, because 4 kHz PWM is enough for this circuit -// and speed changes make a big visible bump -#define HALFSPEED_LEVEL 255 -#define QUARTERSPEED_LEVEL 255 - -#define RAMP_SMOOTH_FLOOR 1 // low levels may be unreliable -#define RAMP_SMOOTH_CEIL 130 -// 10, 30, 50, [70], 90, 110, 130 -// Nichia B35 model: (0.56), 1.4, 8.4, 34.5, [102], 250, 500, 860, (1300) lm -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// safe limit ~75% power -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL -#define SIMPLE_UI_STEPS 5 - -// stop panicking at ~70% power or ~600 lm -#define THERM_FASTER_LEVEL 130 - -#define THERM_CAL_OFFSET 5 - -// the power regulator seems to "jump start" the LEDs all on its own, -// so the firmware doesn't have to -// (and unfortunately the power regulator jumps it a bit too hard) -#define DEFAULT_JUMP_START_LEVEL 1 -#define BLINK_BRIGHTNESS 50 -#define BLINK_ONCE_TIME 12 - -// show each channel while it scroll by in the menu -#define USE_CONFIG_COLORS - -// blink numbers on the main LEDs by default (but allow user to change it) -#define DEFAULT_BLINK_CHANNEL CM_MAIN - -// slow down party strobe; this driver can't pulse for 2ms or less -#define PARTY_STROBE_ONTIME 3 - -// use aux red + aux blue for police strobe -#define USE_POLICE_COLOR_STROBE_MODE -#define POLICE_STROBE_USES_AUX -#define POLICE_COLOR_STROBE_CH1 CM_AUXRED -#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU - -// the default of 26 looks a bit rough, so increase it to make it smoother -#define CANDLE_AMPLITUDE 33 - -// don't blink while ramping -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - -// added for convenience -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/cfg-noctigon-dm11-nofet.h b/spaghetti-monster/anduril/cfg-noctigon-dm11-nofet.h deleted file mode 100644 index b2fdfdb..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-dm11-nofet.h +++ /dev/null @@ -1,49 +0,0 @@ -// Noctigon DM11 (no DD FET) config options for Anduril -// Copyright (C) 2021-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// same support functions as a KR4 -#define HWDEF_C_FILE hwdef-noctigon-kr4-nofet.c -#include "cfg-noctigon-dm11.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0272" -// ATTINY: 1634 - -// turn off the DD FET -#undef PWM_CHANNELS -#define PWM_CHANNELS 1 -#define RAMP_SIZE 150 - -// level_calc.py 5.01 1 149 7135 1 0.3 1740 --pwm dyn:78:16384:255 -#undef PWM1_LEVELS -#define PWM1_LEVELS 0,1,1,1,2,3,3,4,5,6,7,8,9,10,11,13,14,16,17,19,21,23,25,27,29,31,34,36,39,42,44,47,50,53,57,60,63,67,70,74,77,81,85,88,92,96,99,103,107,110,113,117,120,123,126,128,130,133,134,136,137,137,137,137,136,135,133,130,126,122,117,111,104,96,87,76,65,52,38,22,23,25,26,27,28,29,30,32,33,34,36,37,39,40,42,43,45,47,49,51,53,55,57,59,61,63,66,68,70,73,76,78,81,84,87,90,93,96,99,103,106,110,113,117,121,125,129,133,137,142,146,151,155,160,165,170,175,181,186,192,197,203,209,215,222,228,234,241,248,255 -#undef PWM2_LEVELS -#undef PWM_TOPS -#define PWM_TOPS 16383,16383,12404,8140,11462,14700,11041,12947,13795,14111,14124,13946,13641,13248,12791,13418,12808,13057,12385,12428,12358,12209,12000,11746,11459,11147,11158,10793,10708,10576,10173,9998,9800,9585,9527,9278,9023,8901,8634,8486,8216,8053,7881,7615,7440,7261,7009,6832,6656,6422,6196,6031,5819,5615,5419,5190,4973,4803,4571,4386,4179,3955,3745,3549,3340,3145,2940,2729,2513,2312,2109,1903,1697,1491,1286,1070,871,662,459,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#undef DEFAULT_LEVEL -#define DEFAULT_LEVEL 70 -#undef MAX_1x7135 -#define MAX_1x7135 150 - -// make candle mode wobble more -#ifdef CANDLE_AMPLITUDE -#undef CANDLE_AMPLITUDE -#endif -#define CANDLE_AMPLITUDE 33 - - -// slow down party strobe; this driver can't pulse for 1ms or less -// (only needed on no-FET build) -#define PARTY_STROBE_ONTIME 2 - -// jump start a bit higher than base driver -#undef DEFAULT_JUMP_START_LEVEL -#define DEFAULT_JUMP_START_LEVEL 25 - -// stop panicking at ~70% power or ~600 lm -#undef THERM_FASTER_LEVEL -#define THERM_FASTER_LEVEL 130 -#undef MIN_THERM_STEPDOWN -#define MIN_THERM_STEPDOWN 80 // must be > end of dynamic PWM range - diff --git a/spaghetti-monster/anduril/cfg-noctigon-dm11-sbt90.h b/spaghetti-monster/anduril/cfg-noctigon-dm11-sbt90.h deleted file mode 100644 index 9fac446..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-dm11-sbt90.h +++ /dev/null @@ -1,46 +0,0 @@ -// Noctigon DM11-SBT90.2 config options for Anduril -// Copyright (C) 2021-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#include "cfg-noctigon-kr4.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0274" -// ATTINY: 1634 - -// this light has three aux LED channels: R, G, B -#define USE_AUX_RGB_LEDS -// ... and a single LED in the button -#define USE_BUTTON_LED -// don't use aux LEDs while main LED is on -#ifdef USE_INDICATOR_LED_WHILE_RAMPING -#undef USE_INDICATOR_LED_WHILE_RAMPING -#endif - -// power channels: -// - linear: 5A? -// - FET: DD - -#undef DEFAULT_LEVEL -#define DEFAULT_LEVEL 70 - -#undef RAMP_SMOOTH_FLOOR -#define RAMP_SMOOTH_FLOOR 10 // low levels may be unreliable -// 10, 30, 50, [70], 90, 110, 130 -#undef RAMP_DISCRETE_FLOOR -#define RAMP_DISCRETE_FLOOR 10 - -// safe limit ~75% power -#undef SIMPLE_UI_CEIL -#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL - -// stop panicking at ~70% power or ~600 lm -#undef THERM_FASTER_LEVEL -#define THERM_FASTER_LEVEL 130 - -#undef BLINK_BRIGHTNESS -#define BLINK_BRIGHTNESS 50 - -#undef CANDLE_AMPLITUDE -#define CANDLE_AMPLITUDE 30 - diff --git a/spaghetti-monster/anduril/cfg-noctigon-dm11.h b/spaghetti-monster/anduril/cfg-noctigon-dm11.h deleted file mode 100644 index cd6bc9d..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-dm11.h +++ /dev/null @@ -1,88 +0,0 @@ -// Noctigon DM11 config options for Anduril -// Copyright (C) 2021-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0271" -#include "hwdef-noctigon-dm11.h" -#include "hank-cfg.h" -// ATTINY: 1634 - -// this light has three aux LED channels: R, G, B -#define USE_AUX_RGB_LEDS -// ... and a single LED in the button -#define USE_BUTTON_LED -// don't use aux LEDs while main LED is on -#ifdef USE_INDICATOR_LED_WHILE_RAMPING -#undef USE_INDICATOR_LED_WHILE_RAMPING -#endif - -#define RAMP_SIZE 150 - -// power channels: -// - linear: 5A? -// - FET: DD - -// maxreg at 130, dynamic PWM: level_calc.py 5.01 2 149 7135 1 0.3 1740 FET 1 10 3190 --pwm dyn:64:16384:255 -// (plus one extra level at the beginning for moon) -#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255 -#define PWM_TOPS 16383,16383,11750,14690,9183,12439,13615,13955,13877,13560,13093,12529,13291,12513,12756,12769,11893,11747,12085,11725,11329,11316,10851,10713,10518,10282,10016,9729,9428,9298,8971,8794,8459,8257,8043,7715,7497,7275,7052,6753,6538,6260,5994,5798,5501,5271,5006,4758,4525,4268,4030,3775,3508,3263,3010,2752,2517,2256,1998,1763,1512,1249,994,749,497,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#define MIN_THERM_STEPDOWN 66 // should be above highest dyn_pwm level - -#define MAX_1x7135 130 -#define DEFAULT_LEVEL 70 -#define HALFSPEED_LEVEL 12 -#define QUARTERSPEED_LEVEL 4 - -#define RAMP_SMOOTH_FLOOR 10 // low levels may be unreliable -#define RAMP_SMOOTH_CEIL 130 -// 10, 30, 50, [70], 90, 110, 130 -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// safe limit ~75% power -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL -#define SIMPLE_UI_STEPS 5 - -// stop panicking at ~70% power or ~600 lm -#define THERM_FASTER_LEVEL 130 - -#define THERM_CAL_OFFSET 5 - - -// the power regulator is a bit slow, so push it harder for a quick response from off -#define DEFAULT_JUMP_START_LEVEL 21 -#define BLINK_BRIGHTNESS 50 -#define BLINK_ONCE_TIME 12 - -// show each channel while it scroll by in the menu -#define USE_CONFIG_COLORS - -// there is usually no lighted button, so -// blink numbers on the main LEDs by default (but allow user to change it) -#define DEFAULT_BLINK_CHANNEL CM_MAIN - -// slow down party strobe; this driver can't pulse for 1ms or less -// (only needed on no-FET build) -//#define PARTY_STROBE_ONTIME 2 - -// use aux red + aux blue for police strobe -#define USE_POLICE_COLOR_STROBE_MODE -#define POLICE_STROBE_USES_AUX -#define POLICE_COLOR_STROBE_CH1 CM_AUXRED -#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU - -// make candle mode wobble more -#define CANDLE_AMPLITUDE 30 - -// don't blink while ramping -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - -// added for convenience -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/cfg-noctigon-k1-boost.h b/spaghetti-monster/anduril/cfg-noctigon-k1-boost.h deleted file mode 100644 index 0684ce7..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-k1-boost.h +++ /dev/null @@ -1,96 +0,0 @@ -// Noctigon K1 boost config options for Anduril -// Copyright (C) 2019-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0253" -#include "hwdef-noctigon-k1-boost.h" -#include "hank-cfg.h" -// ATTINY: 1634 - -// Most K1 lights can run hotter than default, but the 12V model -// is known to have issues with the driver itself getting too hot. -// This then causes the main LEDs to turn off during use, because the -// boost chip's built-in protection activates. So try to keep the -// temperature relatively low. -#undef DEFAULT_THERM_CEIL -#define DEFAULT_THERM_CEIL 45 - -// this light has three aux LED channels: R, G, B -#define USE_AUX_RGB_LEDS -#define USE_AUX_RGB_LEDS_WHILE_ON 25 -#define USE_INDICATOR_LED_WHILE_RAMPING - -#if 0 // old, 10-bit PWM method -// level_calc.py cube 1 150 7135 0 4 1300 -// (with max_pwm set to 1023) -// (level 0 is usable on this light) -#define RAMP_LENGTH 150 -#define PWM1_LEVELS 0,0,1,1,2,2,3,3,4,5,5,6,7,8,9,10,11,12,13,14,15,16,17,19,20,22,23,25,26,28,30,31,33,35,37,39,42,44,46,48,51,53,56,59,61,64,67,70,73,76,80,83,86,90,94,97,101,105,109,113,117,122,126,130,135,140,144,149,154,159,165,170,175,181,187,193,198,204,211,217,223,230,236,243,250,257,264,271,279,286,294,302,310,318,326,334,343,351,360,369,378,387,397,406,416,426,436,446,456,466,477,488,499,510,521,532,544,555,567,579,591,604,616,629,642,655,668,682,695,709,723,737,751,766,780,795,810,825,841,856,872,888,904,921,937,954,971,988,1005,1023 -#define MAX_1x7135 50 -// don't slow down at low levels; this isn't that sort of light -// (it needs to stay at full speed for the 10-bit PWM to work) -#ifdef USE_DYNAMIC_UNDERCLOCKING -#undef USE_DYNAMIC_UNDERCLOCKING -#endif -#endif - -#define RAMP_SIZE 150 -// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits) -// level_calc.py 5.01 1 150 7135 0 0.2 2000 --pwm 32640 -// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) -#define PWM1_LEVELS 0,1,2,3,4,5,6,7,9,10,12,14,17,19,22,25,28,32,36,41,45,50,56,62,69,76,84,92,101,110,121,132,143,156,169,184,199,215,232,251,270,291,313,336,360,386,414,442,473,505,539,574,612,651,693,736,782,829,880,932,987,1045,1105,1168,1233,1302,1374,1449,1527,1608,1693,1781,1873,1969,2068,2172,2279,2391,2507,2628,2753,2883,3018,3158,3303,3454,3609,3771,3938,4111,4289,4475,4666,4864,5068,5280,5498,5724,5957,6197,6445,6701,6965,7237,7518,7808,8106,8413,8730,9056,9392,9737,10093,10459,10835,11223,11621,12031,12452,12884,13329,13786,14255,14737,15232,15741,16262,16798,17347,17911,18489,19082,19691,20314,20954,21609,22281,22969,23674,24397,25137,25895,26671,27465,28279,29111,29963,30835,31727,32640 -#define MIN_THERM_STEPDOWN 50 -#define DEFAULT_LEVEL 70 -#define MAX_1x7135 150 -// always run at 1/4th speed, because 4 kHz PWM is enough for this circuit -// and speed changes make a big visible bump -#define HALFSPEED_LEVEL 255 -#define QUARTERSPEED_LEVEL 255 - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 130 -// 10, 30, 50, [70], 90, 110, 130 -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// safe limit ~50% power -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL 120 -#define SIMPLE_UI_STEPS 5 - -// stop panicking at ~70% power or ~600 lm -#define THERM_FASTER_LEVEL 130 - -#define THERM_CAL_OFFSET 5 - -#define THERM_RESPONSE_MAGNITUDE 32 // smaller adjustments, this host changes temperature slowly -#define THERM_NEXT_WARNING_THRESHOLD 32 // more error tolerance before adjusting - -// show each channel while it scroll by in the menu -#define USE_CONFIG_COLORS - -// blink numbers on the main LEDs by default (but allow user to change it) -#define DEFAULT_BLINK_CHANNEL CM_MAIN - -// slow down party strobe; this driver can't pulse for 1ms or less -#define PARTY_STROBE_ONTIME 4 - -// use aux red + aux blue for police strobe -#define USE_POLICE_COLOR_STROBE_MODE -#define POLICE_STROBE_USES_AUX -#define POLICE_COLOR_STROBE_CH1 CM_AUXRED -#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU - -// make candle mode wobble more -#define CANDLE_AMPLITUDE 33 - -// the entire ramp is regulated; don't blink halfway up -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - -// added for convenience -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/cfg-noctigon-k1-sbt90.h b/spaghetti-monster/anduril/cfg-noctigon-k1-sbt90.h deleted file mode 100644 index 568ff95..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-k1-sbt90.h +++ /dev/null @@ -1,96 +0,0 @@ -// Noctigon K1-SBT90.2 config options for Anduril -// Copyright (C) 2019-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// (is a K1 host with a KR4-like driver and a really high-powered LED) -#define MODEL_NUMBER "0252" -#include "hwdef-noctigon-k1-sbt90.h" -#include "hank-cfg.h" -// ATTINY: 1634 - -// this light can safely run a bit hotter than most -#undef DEFAULT_THERM_CEIL -#define DEFAULT_THERM_CEIL 55 - -// this light has three aux LED channels: R, G, B -#define USE_AUX_RGB_LEDS -#define USE_AUX_RGB_LEDS_WHILE_ON 10 -#define USE_INDICATOR_LED_WHILE_RAMPING - - -// brightness: -// 0/1023: 0.35 lm -// 1/1023: 2.56 lm -// max regulated: 1740 lm -// FET: ~3700 lm -#define RAMP_SIZE 150 -// FIXME: it should probably have max_regulated at 120, not 130 -// ramp copied from noctigon-kr4 -// nice low lows, but might have visible ripple on some lights: -// maxreg at 130, dynamic PWM: level_calc.py 5.01 2 149 7135 1 0.3 1740 FET 1 10 3190 --pwm dyn:64:16384:255 -// (plus one extra level at the beginning for moon) -#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255 -#define PWM_TOPS 16383,16383,11750,14690,9183,12439,13615,13955,13877,13560,13093,12529,13291,12513,12756,12769,11893,11747,12085,11725,11329,11316,10851,10713,10518,10282,10016,9729,9428,9298,8971,8794,8459,8257,8043,7715,7497,7275,7052,6753,6538,6260,5994,5798,5501,5271,5006,4758,4525,4268,4030,3775,3508,3263,3010,2752,2517,2256,1998,1763,1512,1249,994,749,497,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#define MIN_THERM_STEPDOWN 66 // should be above highest dyn_pwm level - -#define MAX_1x7135 130 -#define DEFAULT_LEVEL 50 -#define HALFSPEED_LEVEL 12 -#define QUARTERSPEED_LEVEL 4 - -#if 0 // original 10-bit ramp -// maxreg at 130: level_calc.py cube 2 150 7135 0 2.5 1740 FET 1 10 2565 -// maxreg at 120: level_calc.py cube 2 150 7135 0 2.5 1740 FET 1 10 3190 -#define PWM1_LEVELS 0,0,1,1,2,2,3,3,4,4,5,6,7,8,9,10,11,13,14,15,17,19,20,22,24,26,28,30,33,35,38,40,43,46,49,52,55,59,62,66,70,74,78,82,86,91,96,100,105,111,116,121,127,133,139,145,151,158,165,172,179,186,193,201,209,217,225,234,243,251,261,270,280,289,299,310,320,331,342,353,364,376,388,400,412,425,438,451,464,478,492,506,521,536,551,566,582,597,614,630,647,664,681,699,717,735,754,772,792,811,831,851,871,892,913,935,956,978,1001,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,51,79,109,138,168,198,229,260,292,324,357,390,423,457,492,527,562,598,634,671,708,746,784,822,861,901,941,982,1023 -#define DEFAULT_LEVEL 46 -#define MAX_1x7135 120 -#define HALFSPEED_LEVEL 10 -#define QUARTERSPEED_LEVEL 2 -#endif - -#define RAMP_SMOOTH_FLOOR 11 // low levels may be unreliable -#define RAMP_SMOOTH_CEIL 130 -// 11 30 [50] 70 90 110 [130] -#define RAMP_DISCRETE_FLOOR 11 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// safe limit ~33% power -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL 120 -#define SIMPLE_UI_STEPS 5 - -// stop panicking at ~40% power or ~1700 lm -#define THERM_FASTER_LEVEL 130 -//#define THERM_NEXT_WARNING_THRESHOLD 16 // accumulate less error before adjusting -//#define THERM_RESPONSE_MAGNITUDE 128 // bigger adjustments - -#define THERM_CAL_OFFSET 5 - -// the power regulator is a bit slow, so push it harder for a quick response from off -// (unsure if necessary, copied from noctigon-kr4) -//#define DEFAULT_JUMP_START_LEVEL 21 -//#define BLINK_BRIGHTNESS DEFAULT_LEVEL -//#define BLINK_ONCE_TIME 12 - -// there is usually no lighted button, so -// blink numbers on the main LEDs by default (but allow user to change it) -#define DEFAULT_BLINK_CHANNEL CM_MAIN - -// normal party strobe speed; this driver can pulse very quickly due to its DD FET -//#define PARTY_STROBE_ONTIME 2 - -// the default of 26 looks a bit rough, so increase it to make it smoother -#define CANDLE_AMPLITUDE 33 - -// don't blink while ramping -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - -// for compatibility with other models -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/cfg-noctigon-k1.h b/spaghetti-monster/anduril/cfg-noctigon-k1.h deleted file mode 100644 index b2d4697..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-k1.h +++ /dev/null @@ -1,83 +0,0 @@ -// Noctigon K1 config options for Anduril -// Copyright (C) 2019-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0251" -// (originally known as Emisar D1S v2) -#include "hwdef-noctigon-k1.h" -#include "hank-cfg.h" -// ATTINY: 1634 - -// this light can safely run a bit hotter than most -#undef DEFAULT_THERM_CEIL -#define DEFAULT_THERM_CEIL 55 - -// this light has three aux LED channels: R, G, B -#define USE_AUX_RGB_LEDS -#define USE_AUX_RGB_LEDS_WHILE_ON 5 -#define USE_INDICATOR_LED_WHILE_RAMPING - - -#define RAMP_SIZE 150 - -// ../../bin/level_calc.py cube 1 150 7135 1 4 1300 -// (with max_pwm set to 1023) -// (level 0 flickers and isn't relevant on a thrower, so it's omitted) -#define RAMP_LENGTH 150 -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,6,7,8,9,10,11,12,13,14,15,16,17,18,20,21,23,24,26,27,29,31,32,34,36,38,40,43,45,47,49,52,54,57,60,62,65,68,71,74,77,81,84,87,91,95,98,102,106,110,114,118,122,127,131,136,141,145,150,155,160,166,171,176,182,188,193,199,205,211,218,224,231,237,244,251,258,265,272,280,287,295,303,310,319,327,335,344,352,361,370,379,388,397,407,416,426,436,446,457,467,477,488,499,510,521,533,544,556,568,580,592,604,617,629,642,655,668,682,695,709,723,737,751,766,781,795,810,826,841,857,872,888,904,921,937,954,971,988,1005,1023 - -#define MAX_1x7135 50 -#define DEFAULT_LEVEL 50 -#define MIN_THERM_STEPDOWN 50 // should be above highest dyn_pwm level -//#define HALFSPEED_LEVEL 12 -//#define QUARTERSPEED_LEVEL 4 -// don't slow down at low levels; this isn't that sort of light -// (it needs to stay at full speed for the 10-bit PWM to work) -#ifdef USE_DYNAMIC_UNDERCLOCKING -#undef USE_DYNAMIC_UNDERCLOCKING -#endif - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 130 -// 10, 30, [50], 70, 90, 110, 130 -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// safe limit ~75% power -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL -#define SIMPLE_UI_STEPS 5 - -// stop panicking at ~70% power or ~600 lm -#define THERM_FASTER_LEVEL 130 - -#define THERM_CAL_OFFSET 5 - -#define THERM_RESPONSE_MAGNITUDE 32 // smaller adjustments, this host changes temperature slowly -#define THERM_NEXT_WARNING_THRESHOLD 32 // more error tolerance before adjusting - -// the power regulator is a bit slow, so push it harder for a quick response from off -#define DEFAULT_JUMP_START_LEVEL 10 -#define BLINK_BRIGHTNESS DEFAULT_LEVEL -#define BLINK_ONCE_TIME 12 - -// show each channel while it scroll by in the menu -#define USE_CONFIG_COLORS - -// there is usually no lighted button, -// so blink numbers on the main LEDs by default (but allow user to change it) -#define DEFAULT_BLINK_CHANNEL CM_MAIN - -// slow down party strobe; this driver can't pulse for 1ms or less -#define PARTY_STROBE_ONTIME 2 - -// make candle mode wobble more -#define CANDLE_AMPLITUDE 32 - -// don't blink while ramping; the entire ramp is regulated -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - diff --git a/spaghetti-monster/anduril/cfg-noctigon-k9.3-219.h b/spaghetti-monster/anduril/cfg-noctigon-k9.3-219.h deleted file mode 100644 index 88abf05..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-k9.3-219.h +++ /dev/null @@ -1,15 +0,0 @@ -// Noctigon K9.3 (reduced FET) config options for Anduril -// Copyright (C) 2020-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#include "cfg-noctigon-k9.3.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0263" -// ATTINY: 1634 - -// main LEDs -#undef PWM2_LEVELS -// 65% FET power -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,7,11,15,19,23,27,32,37,41,46,51,56,61,66,72,78,83,89,95,102,108,115,121,128,135,143,150,157,165 - diff --git a/spaghetti-monster/anduril/cfg-noctigon-k9.3-nofet.h b/spaghetti-monster/anduril/cfg-noctigon-k9.3-nofet.h deleted file mode 100644 index 16504fd..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-k9.3-nofet.h +++ /dev/null @@ -1,12 +0,0 @@ -// Noctigon K9.3 (noFET) config options for Anduril -// Copyright (C) 2020-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// no functional differences from generic emisar-2ch build -// (just use that one instead, this is only here for legacy reasons) -#include "cfg-emisar-2ch.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0262" -// ATTINY: 1634 - diff --git a/spaghetti-monster/anduril/cfg-noctigon-k9.3.c b/spaghetti-monster/anduril/cfg-noctigon-k9.3.c deleted file mode 100644 index d30d397..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-k9.3.c +++ /dev/null @@ -1,108 +0,0 @@ -#error This build is broken. -// Noctigon K9.3 code overrides for Anduril -// Copyright (C) 2020-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -/* - * K9.3 has unusual power channels, so it must override some of FSM's code. - * There are two sets of LEDs: - * 1. Main LEDs: (9 x white LEDs) - * PWM1 (10-bit, linear) - * PWM2 (8-bit, FET, only used on some K9.3 models) - * 2. 2nd LEDs: (3 x white or color LEDs) - * PWM3 (10-bit, linear) - * - * The two sets are not used at the same time... just one or the other, - * depending on the "tint" variable. (0 = main LEDs, other value = 2nd LEDs) - */ -// if the gradual adjustment mechanism doesn't work, disable it here: -//#ifdef USE_SET_LEVEL_GRADUALLY -//#undef USE_SET_LEVEL_GRADUALLY -//#endif - -// this is inserted into fsm-ramping.c :: set_level() -// (it overrides part of the function, but not all of it) -inline void set_level_override(uint8_t level) { - if (level == 0) { // off - PWM1_LVL = 0; - PWM2_LVL = 0; - PWM3_LVL = 0; - // disable both power channels - LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN); - LED2_ENABLE_PORT &= ~(1 << LED2_ENABLE_PIN); - } else { - level --; - - if (! tint) { // main LEDs - // enable this power channel - LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN); - // disable other power channel - LED2_ENABLE_PORT &= ~(1 << LED2_ENABLE_PIN); - // set levels - PWM1_LVL = PWM_GET(pwm1_levels, level); - #ifndef K93_NO_FET - PWM2_LVL = (uint8_t)(PWM_GET(pwm2_levels, level) >> 2); // 8 bits - #endif - PWM3_LVL = 0; - } else { // 2nd LEDs - // disable other power channel - LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN); - // enable this power channel - LED2_ENABLE_PORT |= (1 << LED2_ENABLE_PIN); - // set levels - PWM1_LVL = 0; - PWM2_LVL = 0; - PWM3_LVL = PWM_GET(pwm3_levels, level); - } - } -} - -// override fsm-ramping.c :: gradual_tick() -// (because the power handling on this light is weird) -// call this every frame or every few frames to change brightness very smoothly -void gradual_tick() { - // go by only one ramp level at a time instead of directly to the target - uint8_t gt = gradual_target; - if (gt < actual_level) gt = actual_level - 1; - else if (gt > actual_level) gt = actual_level + 1; - - gt --; // convert 1-based number to 0-based - - PWM_DATATYPE target; - - if (! tint) { // main LED channel - target = PWM_GET(pwm1_levels, gt); - if ((gt < actual_level) // special case for FET-only turbo - && (PWM1_LVL == 0) // (bypass adjustment period for first step) - && (target == PWM_TOP)) PWM1_LVL = PWM_TOP; - else if (PWM1_LVL < target) PWM1_LVL ++; - else if (PWM1_LVL > target) PWM1_LVL --; - - #ifndef K93_NO_FET // skip this on E21A model - target = PWM_GET(pwm2_levels, gt) >> 2; // 8 bits, not 10 - if (PWM2_LVL < target) PWM2_LVL ++; - else if (PWM2_LVL > target) PWM2_LVL --; - #endif - - // did we go far enough to hit the next defined ramp level? - // if so, update the main ramp level tracking var - if ((PWM1_LVL == PWM_GET(pwm1_levels, gt)) - #ifndef K93_NO_FET - && (PWM2_LVL == (PWM_GET(pwm2_levels, gt) >> 2)) - #endif - ) - { actual_level = gt + 1; } - } else { // 2nd LED channel - target = PWM_GET(pwm3_levels, gt); - if (PWM3_LVL < target) PWM3_LVL ++; - else if (PWM3_LVL > target) PWM3_LVL --; - - // did we go far enough to hit the next defined ramp level? - // if so, update the main ramp level tracking var - if ( PWM3_LVL == PWM_GET(pwm3_levels, gt) ) - { actual_level = gt + 1; } - } - -} - diff --git a/spaghetti-monster/anduril/cfg-noctigon-k9.3.h b/spaghetti-monster/anduril/cfg-noctigon-k9.3.h deleted file mode 100644 index ba24504..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-k9.3.h +++ /dev/null @@ -1,111 +0,0 @@ -// Noctigon K9.3 config options for Anduril -// Copyright (C) 2020-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0261" -#include "hwdef-emisar-2ch-fet.h" -#include "hank-cfg.h" -// ATTINY: 1634 - -// 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 -// TODO: the whole "indicator LED" thing needs to be refactored into -// "aux LED(s)" and "button LED(s)" since they work a bit differently -// enabling this option breaks the button LED on D4v2.5 -#ifdef USE_INDICATOR_LED_WHILE_RAMPING -#undef USE_INDICATOR_LED_WHILE_RAMPING -#endif - -// channel modes... -// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO -// enable max brightness out of the box -#define DEFAULT_CHANNEL_MODE CM_BOTH - -#define USE_CONFIG_COLORS - -// blink numbers on the main LEDs by default (but allow user to change it) -#define DEFAULT_BLINK_CHANNEL CM_BOTH - -#define POLICE_COLOR_STROBE_CH1 CM_CH1 -#define POLICE_COLOR_STROBE_CH2 CM_CH2 - -// how much to increase total brightness at middle tint -// (0 = 100% brightness, 64 = 200% brightness) -#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it - - -// main LEDs -// max regulated: 1500 to 2000 lm? -// FET: 5000 to 8000 lm? -// 2nd LEDs -// max regulated: ~1500 lm -#define RAMP_SIZE 150 - -// linear+FET ramp: maxreg at 120/150 -// level_calc.py 5.01 2 150 7135 1 0.1 1872 FET 1 10 5000 --pwm dyn:63:4096:255:3 --clock 8:16:0 -// linear segment -#define PWM1_LEVELS 1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7,7,7,8,8,8,8,9,9,10,10,10,11,12,12,13,14,14,16,17,18,19,20,21,22,24,25,26,28,29,31,33,35,36,38,40,42,45,47,49,52,54,57,60,63,66,69,72,76,79,83,87,91,95,99,103,108,112,117,122,127,133,138,144,150,156,162,169,175,182,189,197,204,212,220,229,237,246,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -// DD FET segment -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,11,17,24,30,36,43,50,57,64,71,79,87,95,103,111,120,129,138,147,157,167,177,187,198,209,220,231,243,255 -// PWM TOPS values -#define PWM3_LEVELS 4096,3798,3504,3214,2926,2645,2368,2098,1834,1579,1333,2304,2017,1744,1486,1242,1174,1700,1505,1323,1155,1037,1299,1163,1038,923,819,967,874,789,712,641,577,655,598,545,497,453,413,456,419,386,355,326,352,326,301,278,296,275,287,267,249,258,265,248,253,256,240,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 - -// linear-only ramp -// level_calc.py 3.11 1 150 7135 1 0.1 1750 --pwm dyn:64:4096:255:3 --clock 8:16:0 -#define PWM4_LEVELS 1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,5,5,6,6,6,7,7,7,8,8,8,9,9,9,10,10,10,10,10,10,11,11,11,11,11,11,11,12,12,12,12,12,12,13,13,13,14,14,14,15,15,16,17,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,33,34,35,37,38,39,41,42,44,45,47,48,50,52,53,55,57,59,61,63,65,67,69,71,73,75,77,79,82,84,86,89,91,94,97,99,102,105,107,110,113,116,119,122,125,128,132,135,138,141,145,148,152,156,159,163,167,170,174,178,182,186,190,195,199,203,208,212,217,221,226,230,235,240,245,250,255 -// PWM_TOPS values for linear-only ramp -#define PWM5_LEVELS 4096,3681,3247,2794,2328,1856,2937,2393,1860,2690,2273,1875,2281,1959,1658,1893,1646,1774,1569,1381,1466,1309,1166,1224,1104,996,1033,942,858,882,810,746,687,634,586,604,561,522,487,454,425,397,409,385,362,341,321,302,311,295,279,286,271,257,263,250,255,258,246,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 - -#define DEFAULT_LEVEL 65 -#define MAX_1x7135 120 -#define MAX_Nx7135 MAX_1x7135 -#define HALFSPEED_LEVEL 16 -#define QUARTERSPEED_LEVEL 8 - -#define RAMP_SMOOTH_FLOOR 10 // level 1 is unreliable (?) -#define RAMP_SMOOTH_CEIL 120 -// 10, 28, 46, [65], 83, 101, 120 -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// 10, 37, [65], 92, 120 -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL -#define SIMPLE_UI_STEPS 5 - -// stop panicking at ~25% power or ~1000 lm -#define THERM_FASTER_LEVEL 100 -#define MIN_THERM_STEPDOWN 60 // should be above highest dyn_pwm level - -#define USE_POLICE_COLOR_STROBE_MODE -#undef TACTICAL_LEVELS -#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe - -// use the brightest setting for strobe -#define STROBE_BRIGHTNESS MAX_LEVEL -// slow down party strobe; this driver can't pulse for 1ms or less -#define PARTY_STROBE_ONTIME 2 -// TODO: change speed per channel mode -// (the FET is really fast, but the regulator is not) -//#undef PARTY_STROBE_ONTIME - -// the default of 26 looks a bit flat, so increase it -#define CANDLE_AMPLITUDE 33 - -// the power regulator is a bit slow, so push it harder for a quick response from off -#define DEFAULT_JUMP_START_LEVEL 35 -#define BLINK_BRIGHTNESS 30 -#define BLINK_ONCE_TIME 12 // longer blink, since main LEDs are slow - -#define THERM_CAL_OFFSET 5 - -// don't blink while ramping -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4-219.h b/spaghetti-monster/anduril/cfg-noctigon-kr4-219.h deleted file mode 100644 index 5d106ef..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-kr4-219.h +++ /dev/null @@ -1,17 +0,0 @@ -// Noctigon KR4 (reduced FET) config options for Anduril -// Copyright (C) 2020-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#include "cfg-noctigon-kr4.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0213" -// ATTINY: 1634 - -// don't turn off first channel at turbo level -#undef PWM1_LEVELS -#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -// 60% FET power -#undef PWM2_LEVELS -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,12,18,25,32,38,45,53,60,68,75,83,91,99,108,117,125,135,144,153 - diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4-219b.h b/spaghetti-monster/anduril/cfg-noctigon-kr4-219b.h deleted file mode 100644 index b242048..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-kr4-219b.h +++ /dev/null @@ -1,17 +0,0 @@ -// Noctigon KR4 (reduced FET) config options for Anduril -// Copyright (C) 2020-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#include "cfg-noctigon-kr4.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0214" -// ATTINY: 1634 - -// don't turn off first channel at turbo level -#undef PWM1_LEVELS -#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -// 50% FET power -#undef PWM2_LEVELS -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,10,15,21,26,32,38,44,50,56,63,69,76,83,90,97,104,112,120,128 - diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4-2ch.h b/spaghetti-monster/anduril/cfg-noctigon-kr4-2ch.h deleted file mode 100644 index 09dd604..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-kr4-2ch.h +++ /dev/null @@ -1,16 +0,0 @@ -// Noctigon KR4 2-channel config options for Anduril -// Copyright (C) 2021-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// (basically the same as Emisar generic 2-channel build, -// but switch on a different pin, and no lighted button) -// ATTINY: 1634 -#include "hwdef-noctigon-kr4-2ch.h" -#include "cfg-emisar-2ch.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0215" - -// the button doesn't light up -#undef USE_BUTTON_LED - diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4-boost.h b/spaghetti-monster/anduril/cfg-noctigon-kr4-boost.h deleted file mode 100644 index 1603acf..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-kr4-boost.h +++ /dev/null @@ -1,16 +0,0 @@ -// Noctigon KR4 (12V) config options for Anduril -// (and Noctigon KR1) -// Copyright (C) 2020-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// the only things different from dm11-boost are: -// - e-switch is on a different pin (defined in hwdef) -// - different model number -#include "cfg-noctigon-dm11-boost.h" -#include "hwdef-noctigon-kr4-boost.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0216" -#include "hank-cfg.h" -// ATTINY: 1634 - diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4-nofet.h b/spaghetti-monster/anduril/cfg-noctigon-kr4-nofet.h deleted file mode 100644 index 6d49a8b..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-kr4-nofet.h +++ /dev/null @@ -1,66 +0,0 @@ -// Noctigon KR4 (no DD FET) config options for Anduril -// Copyright (C) 2020-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// (and Noctigon KR1) -// (and Emisar D4v2 E21A, a.k.a. "D4v2.5") -#define HWDEF_C_FILE hwdef-noctigon-kr4-nofet.c -#include "cfg-noctigon-kr4.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0212" -// ATTINY: 1634 - -// brightness w/ SST-20 4000K LEDs: -// 0/1023: 0.35 lm -// 1/1023: 2.56 lm -// max regulated: 1740 lm -#undef PWM_CHANNELS -#define PWM_CHANNELS 1 -#define RAMP_SIZE 150 -// prioritize low lows, at risk of visible ripple -// level_calc.py 5.01 1 149 7135 1 0.3 1740 --pwm dyn:78:16384:255 -#undef PWM1_LEVELS -#define PWM1_LEVELS 0,1,1,1,2,3,3,4,5,6,7,8,9,10,11,13,14,16,17,19,21,23,25,27,29,31,34,36,39,42,44,47,50,53,57,60,63,67,70,74,77,81,85,88,92,96,99,103,107,110,113,117,120,123,126,128,130,133,134,136,137,137,137,137,136,135,133,130,126,122,117,111,104,96,87,76,65,52,38,22,23,25,26,27,28,29,30,32,33,34,36,37,39,40,42,43,45,47,49,51,53,55,57,59,61,63,66,68,70,73,76,78,81,84,87,90,93,96,99,103,106,110,113,117,121,125,129,133,137,142,146,151,155,160,165,170,175,181,186,192,197,203,209,215,222,228,234,241,248,255 -#undef PWM2_LEVELS -#undef PWM_TOPS -#define PWM_TOPS 16383,16383,12404,8140,11462,14700,11041,12947,13795,14111,14124,13946,13641,13248,12791,13418,12808,13057,12385,12428,12358,12209,12000,11746,11459,11147,11158,10793,10708,10576,10173,9998,9800,9585,9527,9278,9023,8901,8634,8486,8216,8053,7881,7615,7440,7261,7009,6832,6656,6422,6196,6031,5819,5615,5419,5190,4973,4803,4571,4386,4179,3955,3745,3549,3340,3145,2940,2729,2513,2312,2109,1903,1697,1491,1286,1070,871,662,459,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#undef DEFAULT_LEVEL -#define DEFAULT_LEVEL 50 -#undef MAX_1x7135 -#define MAX_1x7135 150 - -#undef RAMP_SMOOTH_FLOOR -#undef RAMP_SMOOTH_CEIL -#undef RAMP_DISCRETE_FLOOR -#undef RAMP_DISCRETE_CEIL -#undef RAMP_DISCRETE_STEPS - -#define RAMP_SMOOTH_FLOOR 11 // low levels may be unreliable -#define RAMP_SMOOTH_CEIL 130 -// 11, 30, [50], 70, 90, 110, 130 (plus [150] on turbo) -#define RAMP_DISCRETE_FLOOR 11 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// safe limit ~1000 lm (can sustain 900 lm) -#undef SIMPLE_UI_FLOOR -#undef SIMPLE_UI_CEIL -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL - - -// slow down party strobe; this driver can't pulse for 1ms or less -// (only needed on no-FET build) -#define PARTY_STROBE_ONTIME 2 - -// jump start a bit higher than base driver -#undef DEFAULT_JUMP_START_LEVEL -#define DEFAULT_JUMP_START_LEVEL 25 - -// stop panicking at ~1300 lm -#undef THERM_FASTER_LEVEL -#define THERM_FASTER_LEVEL 140 -#undef MIN_THERM_STEPDOWN -#define MIN_THERM_STEPDOWN 80 // must be > end of dynamic PWM range - diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4.h b/spaghetti-monster/anduril/cfg-noctigon-kr4.h deleted file mode 100644 index 5b24ef5..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-kr4.h +++ /dev/null @@ -1,89 +0,0 @@ -// Noctigon KR4 config options for Anduril -// (and Emisar D4v2.5, which uses KR4 driver plus a button LED) -// Copyright (C) 2020-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0211" -#include "hwdef-noctigon-kr4.h" -#include "hank-cfg.h" -// ATTINY: 1634 - -// brightness w/ SST-20 4000K LEDs: -// 0/1023: 0.35 lm -// 1/1023: 2.56 lm -// max regulated: 1740 lm -// FET: ~3700 lm -#define RAMP_SIZE 150 - -// nice low lows, but might have visible ripple on some lights: -// maxreg at 130, dynamic PWM: level_calc.py 5.01 2 149 7135 1 0.3 1740 FET 1 10 3190 --pwm dyn:64:16384:255 -// (plus one extra level at the beginning for moon) -#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255 -#define PWM_TOPS 16383,16383,11750,14690,9183,12439,13615,13955,13877,13560,13093,12529,13291,12513,12756,12769,11893,11747,12085,11725,11329,11316,10851,10713,10518,10282,10016,9729,9428,9298,8971,8794,8459,8257,8043,7715,7497,7275,7052,6753,6538,6260,5994,5798,5501,5271,5006,4758,4525,4268,4030,3775,3508,3263,3010,2752,2517,2256,1998,1763,1512,1249,994,749,497,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#define MIN_THERM_STEPDOWN 66 // should be above highest dyn_pwm level - -// less ripple, but lows are a bit higher than ideal: -// maxreg at 130, dynamic PWM: level_calc.py 5.01 2 149 7135 1 0.3 1740 FET 1 10 3190 --pwm dyn:64:4096:255 -// (plus one extra level at the beginning for moon) -//#define PWM1_LEVELS 0,1,1,1,1,1,1,2,2,2,2,3,3,3,4,4,5,5,6,6,7,8,8,9,10,11,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,32,33,33,34,34,34,34,34,34,33,32,31,30,28,26,24,21,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -//#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255 -//#define PWM_TOPS 4095,4095,3760,3403,3020,2611,2176,3582,3062,2515,1940,3221,2761,2283,2998,2584,3004,2631,2899,2555,2735,2836,2538,2606,2636,2638,2387,2382,2361,2328,2286,2238,2185,2129,2070,2010,1949,1887,1826,1766,1706,1648,1591,1536,1482,1429,1379,1329,1242,1199,1122,1084,1016,953,895,842,791,723,659,602,549,482,422,367,302,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 - -#define MAX_1x7135 130 -#define DEFAULT_LEVEL 50 -#define HALFSPEED_LEVEL 12 -#define QUARTERSPEED_LEVEL 4 - -#define RAMP_SMOOTH_FLOOR 11 // low levels may be unreliable -#define RAMP_SMOOTH_CEIL 130 -// 11 30 [50] 70 90 110 [130] -#define RAMP_DISCRETE_FLOOR 11 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// safe limit ~30% power / ~1300 lm (can sustain 900 lm) -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL 120 -#define SIMPLE_UI_STEPS 5 - -// stop panicking at ~1300 lm -#define THERM_FASTER_LEVEL 120 - -#define THERM_CAL_OFFSET 5 - - -// the power regulator is a bit slow, so push it harder for a quick response from off -#define DEFAULT_JUMP_START_LEVEL 21 -#define BLINK_BRIGHTNESS DEFAULT_LEVEL -#define BLINK_ONCE_TIME 12 - -// show each channel while it scroll by in the menu -#define USE_CONFIG_COLORS - -// there is usually no lighted button, so -// blink numbers on the main LEDs by default (but allow user to change it) -#define DEFAULT_BLINK_CHANNEL CM_MAIN - -// slow down party strobe; this driver can't pulse for 1ms or less -// (only needed on no-FET build) -//#define PARTY_STROBE_ONTIME 2 - -// use aux red + aux blue for police strobe -#define USE_POLICE_COLOR_STROBE_MODE -#define POLICE_STROBE_USES_AUX -#define POLICE_COLOR_STROBE_CH1 CM_AUXRED -#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU - -// the default of 26 looks a bit rough, so increase it to make it smoother -#define CANDLE_AMPLITUDE 33 - -// don't blink while ramping -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - -// can't reset the normal way because power is connected before the button -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/cfg-noctigon-m44.h b/spaghetti-monster/anduril/cfg-noctigon-m44.h deleted file mode 100644 index 88bf628..0000000 --- a/spaghetti-monster/anduril/cfg-noctigon-m44.h +++ /dev/null @@ -1,134 +0,0 @@ -// Noctigon M44 config options for Anduril -// Copyright (C) 2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0143" -#include "hwdef-noctigon-m44.h" -#include "hank-cfg.h" -// ATTINY: 1634 - -// 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 -// TODO: the whole "indicator LED" thing needs to be refactored into -// "aux LED(s)" and "button LED(s)" since they work a bit differently -// enabling this option breaks the button LED on D4v2.5 -#ifdef USE_INDICATOR_LED_WHILE_RAMPING -#undef USE_INDICATOR_LED_WHILE_RAMPING -#endif - -// channel modes... -// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO -// enable max brightness out of the box -#define DEFAULT_CHANNEL_MODE CM_BOTH - -//#define FACTORY_RESET_WARN_CHANNEL CM_CH2 -//#define FACTORY_RESET_SUCCESS_CHANNEL CM_BOTH - -#define USE_CONFIG_COLORS -//#define CONFIG_WAITING_CHANNEL CM_CH2 -//#define CONFIG_BLINK_CHANNEL CM_BOTH - -// blink numbers on the main LEDs by default (but allow user to change it) -#define DEFAULT_BLINK_CHANNEL CM_BOTH - -#define POLICE_COLOR_STROBE_CH1 CM_CH1 -#define POLICE_COLOR_STROBE_CH2 CM_CH2 -// aux red + aux blue are the correct colors, but are dim -//#define POLICE_COLOR_STROBE_CH1 CM_AUXRED -//#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU - -// how much to increase total brightness at middle tint -// (0 = 100% brightness, 64 = 200% brightness) -#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it - -// channel 1 -// output: unknown, 6000 lm? -// channel 2 -// output: unknown, 6000 lm? -#define RAMP_SIZE 150 -#if 0 // optimized hand-tweaked ramp from before PWM+DSM update -// "100% power" ramp -// level_calc.py 5.01 1 150 7135 0 2.0 5000 --pwm dyn:64:16384:511:5 -// (with manual tweaks) -#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,6,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,10,10,11,11,12,12,13,14,15,16,17,18,19,20,21,22,23,24,26,27,28,29,31,32,34,35,37,39,40,42,44,46,48,50,52,54,57,59,62,64,67,69,72,75,78,81,84,87,91,94,98,101,105,109,113,117,121,126,130,135,140,145,150,155,160,165,171,177,183,189,195,201,208,214,221,228,236,243,251,258,266,274,283,291,300,309,318,328,337,347,357,368,378,389,400,411,423,435,447,459,472,484,498,511 -#define PWM_TOPS 16383,16383,10002,11695,8083,9374,9793,7993,8291,7017,7180,6235,5431,5556,4927,4385,3916,3511,3159,2852,2585,2349,2142,1958,1795,1648,1517,1398,1290,1193,1104,1023,950,883,822,765,714,667,623,582,632,610,588,566,544,512,535,505,523,494,509,480,492,502,509,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511 -// max "200% power" ramp and tops -// 1-130: 0 to 100% power -// level_calc.py 5.01 1 130 7135 2 0.2 2000 --pwm dyn:64:16383:511 -// 131 to 150: 101% to 200% power -// level_calc.py 6.44 1 150 7135 1 0.2 2000 --pwm dyn:74:16383:1022 -//#define PWM2_LEVELS 2,2,2,3,3,4,4,5,6,7,8,9,10,11,13,14,16,17,19,21,23,25,28,30,33,35,38,41,44,47,50,54,57,60,64,67,71,74,78,81,84,88,91,94,97,99,101,103,105,106,107,107,107,106,105,102,99,95,90,84,77,68,58,47,34,36,38,40,42,44,47,49,52,54,57,60,63,66,69,73,76,80,83,87,91,96,100,104,109,114,119,124,130,135,141,147,153,160,166,173,180,187,195,203,211,219,228,236,245,255,264,274,285,295,306,317,329,340,353,365,378,391,405,419,433,448,463,479,495,511,530,550,570,591,612,634,657,680,705,730,755,782,809,837,865,895,925,957,989,1022 -//#define PWM3_LEVELS 16383,13234,9781,13826,9593,13434,9973,12021,12900,13193,13150,12899,12508,12023,12666,11982,12181,11422,11393,11247,11018,10731,10826,10434,10365,9927,9767,9565,9332,9076,8806,8693,8395,8096,7928,7626,7439,7143,6948,6665,6393,6203,5946,5700,5465,5187,4926,4681,4451,4195,3957,3700,3463,3213,2983,2718,2476,2231,1986,1742,1501,1245,997,756,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511 -#define MIN_THERM_STEPDOWN 65 // should be above highest dyn_pwm level -#define DEFAULT_LEVEL 70 -#define MAX_1x7135 150 -#define HALFSPEED_LEVEL 10 -#define QUARTERSPEED_LEVEL 2 -#endif // end old ramp config - -// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits) -// level_calc.py 5.01 1 150 7135 0 0.2 2000 --pwm 32640 -// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM) -#define PWM1_LEVELS 0,1,2,3,4,5,6,7,9,10,12,14,17,19,22,25,28,32,36,41,45,50,56,62,69,76,84,92,101,110,121,132,143,156,169,184,199,215,232,251,270,291,313,336,360,386,414,442,473,505,539,574,612,651,693,736,782,829,880,932,987,1045,1105,1168,1233,1302,1374,1449,1527,1608,1693,1781,1873,1969,2068,2172,2279,2391,2507,2628,2753,2883,3018,3158,3303,3454,3609,3771,3938,4111,4289,4475,4666,4864,5068,5280,5498,5724,5957,6197,6445,6701,6965,7237,7518,7808,8106,8413,8730,9056,9392,9737,10093,10459,10835,11223,11621,12031,12452,12884,13329,13786,14255,14737,15232,15741,16262,16798,17347,17911,18489,19082,19691,20314,20954,21609,22281,22969,23674,24397,25137,25895,26671,27465,28279,29111,29963,30835,31727,32640 - -#define MIN_THERM_STEPDOWN 50 -#define DEFAULT_LEVEL 70 -#define MAX_1x7135 150 -// always run at 1/4th speed, because 4 kHz PWM is enough for this circuit -// and speed changes make a big visible bump -#define HALFSPEED_LEVEL 255 -#define QUARTERSPEED_LEVEL 255 - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 130 -// 10, 30, 50, [70], 90, 110, 130 -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// 10 40 [70] 100 130 -#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR -#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL -#define SIMPLE_UI_STEPS 5 - -// stop panicking at ~???? lm -#define THERM_FASTER_LEVEL 130 - -#define USE_POLICE_COLOR_STROBE_MODE -#undef TACTICAL_LEVELS -#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe - -// use the brightest setting for strobe -#define STROBE_BRIGHTNESS MAX_LEVEL -// slow down party strobe; this driver takes a while to start making any light -#define PARTY_STROBE_ONTIME 12 -//#define STROBE_OFF_LEVEL 1 // nope, this makes strobe blurry -// bike strobe needs a longer pulse too? nope -#define BIKE_STROBE_ONTIME 8 - -// the default of 26 looks a bit flat, so increase it -#define CANDLE_AMPLITUDE 33 - -// party strobe, tac strobe, police, lightning, candle, bike -#define DEFAULT_STROBE_CHANNELS CM_BOTH,CM_BOTH,CM_BOTH,CM_AUTO,CM_AUTO,CM_AUTO - -// the power regulator is a bit slow, so push it harder for a quick response from off -//#define DEFAULT_JUMP_START_LEVEL 50 -//#define JUMP_START_TIME 50 -#define BLINK_BRIGHTNESS 30 -#define BLINK_ONCE_TIME 16 // longer blink, since the boost driver is slow - -#define THERM_CAL_OFFSET 5 - -// don't blink while ramping -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - -// for consistency with KR4 (not otherwise necessary though) -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h b/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h deleted file mode 100644 index 440a03d..0000000 --- a/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h +++ /dev/null @@ -1,125 +0,0 @@ -// Sofirn LT1S Pro config file for Anduril -// Copyright (C) 2022-2023 (FIXME) -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0623" -#include "hwdef-sofirn-lt1s-pro.h" -// ATTINY: 1616 - -// off mode: low (1) -// lockout: blinking (3) -// Standby power usage: -// - aux high: 6.9 mA (30 days) -// - aux low: 0.16 mA (3.5 years) -// - red moon: 2.17 mA (96 days) -// - white moon: 1.47 mA (141 days) -// Low mode isn't bright enough to be useful on this light, -// but at least it doesn't drain the battery 3X faster than moon mode. -// (it seriously would be more practical to just use moon instead) -#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) - -// channel modes... -// CM_WHITE, CM_AUTO2, CM_AUTO3, CM_RED, CM_WHITE_RED -#define DEFAULT_CHANNEL_MODE CM_AUTO3 - -#define FACTORY_RESET_WARN_CHANNEL CM_RED -#define FACTORY_RESET_SUCCESS_CHANNEL CM_WHITE - -#define CONFIG_WAITING_CHANNEL CM_RED -#define CONFIG_BLINK_CHANNEL CM_WHITE - -// blink numbers on the main LEDs by default (but allow user to change it) -#define DEFAULT_BLINK_CHANNEL CM_RED - -// how much to increase total brightness at middle tint -// (0 = 100% brightness, 64 = 200% brightness) -// seems unnecessary on this light -#define TINT_RAMPING_CORRECTION 0 - -#define RAMP_SIZE 150 -// use dynamic PWM instead of plain 8-bit -// (so we can get lower lows and a smoother ramp) -// (also, red LEDs use a QX7138 chip which has max PWM speed of 10 kHz, -// and it behaves erratically at full speed, -// so PWM here is 576 clock cycles long to keep the speed low enough) -// -// This first ramp seems a bit too low: 0.2 / 1.9 / 10 / 37 / 109 / 272 / 600 lm -// level_calc.py 5.99 1 150 7135 1 0.2 600 --pwm dyn:77:16383:575:3 -//#define PWM_LEVELS 1,1,2,2,3,4,4,5,6,6,7,8,9,9,10,11,11,12,13,13,14,15,15,16,16,17,18,18,19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,24,24,24,25,26,26,27,28,29,30,32,33,34,36,38,40,42,44,46,48,50,52,55,57,59,62,65,68,70,73,77,80,83,86,90,94,97,101,105,110,114,118,123,128,133,138,143,148,154,160,166,172,178,185,191,198,205,213,220,228,236,244,252,261,270,279,289,298,308,319,329,340,351,363,374,386,399,411,424,438,452,466,480,495,510,526,542,558,575 -//#define PWM_TOPS 16383,10869,13246,8043,11458,12772,10093,11043,11450,9664,9991,10091,10048,8868,8838,8730,7814,7724,7589,6864,6748,6604,6024,5899,5398,5287,5159,4754,4638,4287,3963,3876,3594,3511,3265,3038,2829,2770,2586,2417,2260,2115,1981,1857,1742,1636,1537,1445,1360,1281,1207,1138,1073,1013,957,904,855,848,803,760,720,714,677,643,637,630,599,592,585,577,569,579,570,560,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575 -// -// This ramp is a bit higher: 0.2 / 3 / 19 / 61 / 152 / 320 / 600 lm -// level_calc.py 4.001 1 150 7135 1 0.2 600 --pwm dyn:78:16383:575:3.333 -#define PWM1_LEVELS 1,1,2,4,5,6,7,8,9,10,12,13,14,15,17,18,19,21,22,23,24,25,26,27,28,29,30,31,32,33,33,34,34,35,35,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,37,37,38,38,39,40,40,41,43,44,45,47,48,50,52,54,56,59,62,64,67,69,72,75,78,81,84,87,90,94,97,101,104,108,112,116,120,124,128,133,137,142,147,151,156,161,167,172,177,183,189,194,200,206,213,219,226,232,239,246,253,260,268,275,283,291,299,307,316,324,333,342,351,361,370,380,390,400,410,420,431,442,453,464,476,487,499,511,523,536,549,562,575 -#define PWM_TOPS 16383,8174,7823,14429,13603,12806,12046,11328,10652,10017,10402,9742,9134,8575,8615,8089,7605,7536,7093,6684,6307,5959,5636,5337,5060,4802,4562,4337,4127,3929,3633,3468,3216,3077,2862,2744,2559,2390,2234,2091,1960,1838,1727,1623,1528,1439,1357,1280,1209,1143,1081,1024,970,919,872,828,787,770,732,716,682,668,654,624,611,613,600,587,587,574,573,571,569,566,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575 -// TODO? 200% power at top of ramp on white blend mode -// 2nd table handles "200% power" turbo -//#define PWM2_LEVELS ... -// tops for PWM2 -//#define PWM3_LEVELS ... -#define MAX_1x7135 75 -#define MIN_THERM_STEPDOWN 75 // should be above highest dyn_pwm level -#define HALFSPEED_LEVEL 12 -#define QUARTERSPEED_LEVEL 5 - -// the default of 26 looks a bit flat, so increase it -#define CANDLE_AMPLITUDE 40 - -// override default ramp style -#undef RAMP_STYLE -#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped -// set floor and ceiling as far apart as possible -// because this lantern isn't overpowered -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 150 -//#define RAMP_DISCRETE_FLOOR 17 // 17 50 83 116 150 -#define RAMP_DISCRETE_FLOOR 1 // 1 25 50 75 100 125 150 -#define RAMP_DISCRETE_CEIL 150 -#define RAMP_DISCRETE_STEPS 7 - -// LT1S can handle heat well, so don't limit simple mode -//#define SIMPLE_UI_FLOOR 10 // 10 45 80 115 150 -#define SIMPLE_UI_FLOOR 1 -#define SIMPLE_UI_CEIL 150 -#define SIMPLE_UI_STEPS 7 - -// Allow 3C (or 6C) in Simple UI (toggle smooth or stepped ramping) -#define USE_SIMPLE_UI_RAMPING_TOGGLE - -// allow Aux Config and Strobe Modes in Simple UI -#define USE_EXTENDED_SIMPLE_UI - -// turn on at med-low brightness by default (level 50/150, or ramp step 3/7) -// (also sets lockout mode 2H to a useful level) -#define DEFAULT_MANUAL_MEMORY 50 -// reset to default after being off for 10 minutes -#define DEFAULT_MANUAL_MEMORY_TIMER 10 - -// enable 2 click turbo (Anduril 1 style) -#define DEFAULT_2C_STYLE 1 - -#define USE_SOS_MODE -#define USE_SOS_MODE_IN_BLINKY_GROUP - -#define USE_POLICE_COLOR_STROBE_MODE -#define POLICE_COLOR_STROBE_CH1 CM_RED -#define POLICE_COLOR_STROBE_CH2 CM_WHITE - -#undef TACTICAL_LEVELS -#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe - -// don't blink while ramping -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif -#ifdef BLINK_AT_RAMP_FLOOR -#undef BLINK_AT_RAMP_FLOOR -#endif -#ifdef BLINK_AT_RAMP_CEIL -#undef BLINK_AT_RAMP_CEIL -#endif -// without this, it's really hard to tell when ramping up stops -#define BLINK_AT_RAMP_CEIL - -#define USE_SOFT_FACTORY_RESET diff --git a/spaghetti-monster/anduril/cfg-sofirn-sc21-pro.h b/spaghetti-monster/anduril/cfg-sofirn-sc21-pro.h deleted file mode 100644 index 8fd2dee..0000000 --- a/spaghetti-monster/anduril/cfg-sofirn-sc21-pro.h +++ /dev/null @@ -1,14 +0,0 @@ -// Sofirn SC21 Pro - same setup as a Wurkkos TS10, but with the aux indicator on while ramping -// Copyright (C) 2022-2023 (FIXME) -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#include "cfg-wurkkos-ts10.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0632" -// ATTINY: 1616 - -// turn on the aux LED while main LED is on -#ifndef USE_INDICATOR_LED_WHILE_RAMPING -#define USE_INDICATOR_LED_WHILE_RAMPING -#endif diff --git a/spaghetti-monster/anduril/cfg-sofirn-sp10-pro.h b/spaghetti-monster/anduril/cfg-sofirn-sp10-pro.h deleted file mode 100644 index 0e2f28d..0000000 --- a/spaghetti-monster/anduril/cfg-sofirn-sp10-pro.h +++ /dev/null @@ -1,81 +0,0 @@ -// Sofirn SP10 Pro config options for Anduril -// Copyright (C) 2022-2023 (original author TBD), Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0631" -#include "hwdef-sofirn-sp10-pro.h" -// ATTINY: 1616 - -// 1....15: level_calc.py 3.01 1 15 7135 1 0.1 2 --pwm dyn:15:64:64 -// 16..150: level_calc.py 5.01 1 135 7135 1 2 800 --pwm dyn:49:3072:255:3.0 -#define RAMP_SIZE 150 -#define _PWM1_LEVELS_ 1, 2, 4, 6, 9,12,15,19,23,28,34,41,48,55,64 -#define _PWM1_TOPS_ 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64 -#define _PWM2_LEVELS_ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 13, 13, 14, 15, 15, 16, 17, 18, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 33, 34, 35, 37, 38, 39, 41, 42, 44, 46, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 70, 72, 74, 77, 79, 82, 85, 88, 90, 93, 96, 99,103,106,109,113,116,120,123,127,131,135,139,143,147,151,156,160,165,170,175,180,185,190,195,201,206,212,218,223,230,236,242,248,255 -#define _PWM2_TOPS_ 3072,1960,2372,1476,2097,1572,1920,1570,1777,1524,1646,1454,1286,1369,1234,1115,1011,918,837,894,823,759,702,650,603,560,522,487,455,425,398,374,351,330,310,292,275,259,280,265,251,266,253,240,252,240,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#define PWM1_LEVELS _PWM1_LEVELS_,_PWM2_TOPS_ -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,_PWM2_LEVELS_ -#define PWM_TOPS _PWM1_TOPS_,_PWM2_TOPS_ - -#define MAX_1x7135 15 -#define HALFSPEED_LEVEL 15 -#define QUARTERSPEED_LEVEL 15 -#define DEFAULT_LEVEL 50 - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 150 -// 1 25 50 [75] 100 125 150 -#define RAMP_DISCRETE_FLOOR 1 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// at Sofirn's request, use max (150) for the Simple UI ceiling -// 15 48 [82] 116 150 -#define SIMPLE_UI_FLOOR MAX_1x7135 -#define SIMPLE_UI_CEIL 150 -#define SIMPLE_UI_STEPS 5 - -// turn on at ~6 lm by default (level 50/150, or ramp step 2/5 or 3/7) -// (also sets lockout mode 2H to a useful level) -#define DEFAULT_MANUAL_MEMORY 50 -// reset to default after being off for 10 minutes -#define DEFAULT_MANUAL_MEMORY_TIMER 10 - -// enable SOS in the blinkies group -#define USE_SOS_MODE -#define USE_SOS_MODE_IN_BLINKY_GROUP - -// Allow 3C in Simple UI for switching between smooth and stepped ramping -#define USE_SIMPLE_UI_RAMPING_TOGGLE - -// and finally, set the default ramp style to Stepped -#undef RAMP_STYLE -#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped - -// stop panicking at ~30% power -#define THERM_FASTER_LEVEL 105 -#define MIN_THERM_STEPDOWN 65 // must be > end of dynamic PWM range - -// slow down party strobe; this driver can't pulse for too short a time -//#define PARTY_STROBE_ONTIME 8 -#define STROBE_OFF_LEVEL 1 // keep the regulator chip on between pulses - -// the default of 26 looks a bit flat, so increase it -#define CANDLE_AMPLITUDE 50 - -// enable 2 click turbo (replaces USE_2C_MAX_TURBO) -#define DEFAULT_2C_STYLE 1 - -// don't blink during the ramp or at the ceiling -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif -#ifdef BLINK_AT_RAMP_CEIL -#undef BLINK_AT_RAMP_CEIL -#endif - - -// enable factory reset on 13H without loosening tailcap -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/cfg-sofirn-sp36-t1616.h b/spaghetti-monster/anduril/cfg-sofirn-sp36-t1616.h deleted file mode 100644 index cb29e4f..0000000 --- a/spaghetti-monster/anduril/cfg-sofirn-sp36-t1616.h +++ /dev/null @@ -1,37 +0,0 @@ -// Sofirn SP36 (small Q8) config options for Anduril using the Attiny1616 -// Copyright (C) 2021-2023 (FIXME) -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// same as the BLF Q8, mostly -#include "cfg-blf-q8-t1616.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0614" -// ATTINY: 1616 - -// voltage readings were a little high with the Q8 value -#undef VOLTAGE_FUDGE_FACTOR -#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V, not 0.35V - -// the high button LED mode on this light uses too much power -// off mode: low (1) -// lockout: blinking (3) -#ifdef INDICATOR_LED_DEFAULT_MODE -#undef INDICATOR_LED_DEFAULT_MODE -#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) -#endif - -// don't blink during the ramp; the button LED brightness is sufficient -// to indicate which power channel(s) are being used -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif -#ifdef BLINK_AT_RAMP_CEIL -#undef BLINK_AT_RAMP_CEIL -#endif - -// stop panicking at ~60% power or ~3000 lm -#ifdef THERM_FASTER_LEVEL -#undef THERM_FASTER_LEVEL -#endif -#define THERM_FASTER_LEVEL 130 diff --git a/spaghetti-monster/anduril/cfg-sofirn-sp36.h b/spaghetti-monster/anduril/cfg-sofirn-sp36.h deleted file mode 100644 index 3661686..0000000 --- a/spaghetti-monster/anduril/cfg-sofirn-sp36.h +++ /dev/null @@ -1,36 +0,0 @@ -// Sofirn SP36 (small Q8) config options for Anduril -// Copyright (C) 2018-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// same as the BLF Q8, mostly -#include "cfg-blf-q8.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0612" - -// voltage readings were a little high with the Q8 value -#undef VOLTAGE_FUDGE_FACTOR -#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V, not 0.35V - -// the high button LED mode on this light uses too much power -// off mode: low (1) -// lockout: blinking (3) -#ifdef INDICATOR_LED_DEFAULT_MODE -#undef INDICATOR_LED_DEFAULT_MODE -#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) -#endif - -// don't blink during the ramp; the button LED brightness is sufficient -// to indicate which power channel(s) are being used -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif -#ifdef BLINK_AT_RAMP_CEIL -#undef BLINK_AT_RAMP_CEIL -#endif - -// stop panicking at ~60% power or ~3000 lm -#ifdef THERM_FASTER_LEVEL -#undef THERM_FASTER_LEVEL -#endif -#define THERM_FASTER_LEVEL 130 diff --git a/spaghetti-monster/anduril/cfg-thefreeman-boost-fwaa.h b/spaghetti-monster/anduril/cfg-thefreeman-boost-fwaa.h deleted file mode 100755 index 460346e..0000000 --- a/spaghetti-monster/anduril/cfg-thefreeman-boost-fwaa.h +++ /dev/null @@ -1,116 +0,0 @@ -// thefreeman's BCK-FWAA-MP3432 (li-ion / AA) -// Copyright (C) 2023 TBD (thefreeman), Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "1632" -#include "hwdef-thefreeman-boost-fwaa-mp3432-hdr-dac-rgb.h" -// ATTINY: 1616 - -// HPRsense : 4.2+0.3+20 = 24.5mR (DMN1004UFDF+trace resistance+20mR) -// R1=165k Vsense=49.02 Iout=2001mA -// LPRsense : 2R -// transition DAC lvl 14, ramp lvl 51 -// fifth power ramp 0.06mA to 2001mA - -#define RAMP_SIZE 150 - -// 4 ramp segments: -// - low 0.55V -// - low 2.5V -// - high 0.55V -// - high 2.5V -// PWM1: DAC Data -#define PWM1_LEVELS 2, 3, 4, 5, 6, 8, 9, 11, 14, 16, 19, 23, 26, 31, 35, 41, 47, 54, 61, 69, 78, 89,100,112,125,140,155,173,191,212,234, \ - 56, 62, 68, 74, 82, 89, 97,106,115,125,136,147,159,172,186,200,215,232,249, \ - 14, 15, 17, 18, 19, 20, 22, 23, 25, 26, 28, 30, 32, 34, 36, 38, 40, 43, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81, 86, 90, 95, 99,104,109,114,120,126,131,138,144,150,157,164,171,179,187,195,203,212,221,230,239,249, \ - 57, 59, 61, 64, 66, 69, 72, 74, 77, 80, 83, 86, 90, 93, 96,100,103,107,111,115,119,123,127,132,136,141,145,150,155,160,166,171,176,182,188,194,200,206,213,219,226,233,240,247,255 -// PWM Tops: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20) -#define PWM_TOPS 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \ - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 - -#define MAX_1x7135 50 -#define DEFAULT_LEVEL 44 -#define HDR_ENABLE_LEVEL_MIN 51 // when HDR FET turns ON - -// no PWM, so MCU clock speed can be slow -#define HALFSPEED_LEVEL 46 -#define QUARTERSPEED_LEVEL 45 // seems to run fine at 10kHz/4, try reducing more - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 130 // ~50% power, ~??? mA / ??? lm -#define RAMP_DISCRETE_FLOOR 1 -#define RAMP_DISCRETE_CEIL 130 -#define RAMP_DISCRETE_STEPS 7 - -// 20 [45] 70 95 120 -#define SIMPLE_UI_FLOOR 20 -#define SIMPLE_UI_CEIL 120 // ~37% power, ~??? mA / ??? lm -#define SIMPLE_UI_STEPS 5 - -// don't blink mid-ramp -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - -// thermal config - -// temperature limit -#define THERM_FASTER_LEVEL 130 -#define MIN_THERM_STEPDOWN MAX_1x7135 - -//#define THERM_LOOKAHEAD 2 // 4 by default -> decrease for longer turbo -//#define THERM_NEXT_WARNING_THRESHOLD 48 // 24 by default -> increase for fewer adjustments (more stable output on large time scale) -//#define THERM_RESPONSE_MAGNITUDE 16 // 64 by default -> decrease for smaller adjustments (removes dip post turbo) -//#define THERM_WINDOW_SIZE 1 // 2 by default -> decrease for tighter temperature regulation - - -// UI - -//#define SIMPLE_UI_ACTIVE 0 // advanced UI by default - -// allow Aux Config and Strobe Modes in Simple UI -#define USE_EXTENDED_SIMPLE_UI - -// Allow 3C in Simple UI for switching between smooth and stepped ramping -#define USE_SIMPLE_UI_RAMPING_TOGGLE - -#define DEFAULT_2C_STYLE 1 // enable 2 click turbo - - -// AUX - -// this light has three aux LED channels: R, G, B -#define USE_AUX_RGB_LEDS - -// show each channel while it scroll by in the menu -#define USE_CONFIG_COLORS - -// blink numbers on the main LEDs by default -#define DEFAULT_BLINK_CHANNEL CM_MAIN - -// use aux red + aux blue for police strobe -#define USE_POLICE_COLOR_STROBE_MODE -#define POLICE_STROBE_USES_AUX -#define POLICE_COLOR_STROBE_CH1 CM_AUXRED -#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU - -// the aux LEDs are front-facing, so turn them off while main LEDs are on -#ifdef USE_INDICATOR_LED_WHILE_RAMPING -#undef USE_INDICATOR_LED_WHILE_RAMPING -#endif - - -// Misc - -#define PARTY_STROBE_ONTIME 1 // slow down party strobe -#define STROBE_OFF_LEVEL 1 // keep the regulator chip on between pulses - -// smoother candle mode with bigger oscillations -#define CANDLE_AMPLITUDE 40 - -// enable 13H factory reset so it can be used on tail e-switch lights -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/cfg-thefreeman-boost21-6a.h b/spaghetti-monster/anduril/cfg-thefreeman-boost21-6a.h deleted file mode 100644 index 66cc2d0..0000000 --- a/spaghetti-monster/anduril/cfg-thefreeman-boost21-6a.h +++ /dev/null @@ -1,119 +0,0 @@ -// thefreeman's BST21 BST20-FWxA (no button LED) -// Copyright (C) 2023 TBD (thefreeman), Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "1631" -#include "hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.h" -// ATTINY: 1616 - -// HPRsense : 1.7+0.3+5 = 7mR (DMN22M5UFG+trace resistance+5mR) -// Vsense=42.46mV, R1= 191k -// LPRsense : 1R -// transition DAC level 8, ramp level 45 -// fifth power ramp 0.1mA to 6066mA - -#define RAMP_SIZE 150 - -// 4 ramp segments: -// - low 0.55V -// - low 2.5V -// - high 0.55V -// - high 2.5V -// PWM1: DAC Data -#define PWM1_LEVELS 2, 3, 4, 5, 7, 9, 11, 13, 16, 19, 23, 28, 33, 39, 45, 53, 61, 71, 81, 93,106,121,137,155,175,196,220,246, \ - 60, 67, 74, 82, 91,100,110,121,133,146,159,174,190,207,224,244, \ - 8, 9, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 23, 24, 26, 27, 29, 31, 33, 35, 37, 40, 42, 45, 47, 50, 53, 56, 59, 62, 66, 69, 73, 77, 81, 85, 90, 94, 99,104,109,114,120,126,132,138,144,151,158,165,173,180,188,196,205,214,223,232,242,252, \ - 57, 60, 62, 65, 67, 70, 73, 76, 78, 82, 85, 88, 91, 95, 98,102,105,109,113,117,121,126,130,135,139,144,149,154,159,164,170,175,181,187,193,199,206,212,219,225,232,240,247,255 -// PWM Tops: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20) -#define PWM_TOPS 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \ - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 - -#define MAX_1x7135 44 -#define DEFAULT_LEVEL 44 -#define HDR_ENABLE_LEVEL_MIN 45 // when HDR FET turns ON - -// no PWM, so MCU clock speed can be slow -#define HALFSPEED_LEVEL 41 -#define QUARTERSPEED_LEVEL 40 // seems to run fine at 10kHz/4, try reducing more - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 130 // 50% / 3A / 1000 lm -// 1 22 [44] 65 87 108 130 -#define RAMP_DISCRETE_FLOOR 1 -#define RAMP_DISCRETE_CEIL 130 -#define RAMP_DISCRETE_STEPS 7 - -// 20 [45] 70 95 120 -#define SIMPLE_UI_FLOOR 20 -#define SIMPLE_UI_CEIL 120 // ~2.25A / ~750 lm -#define SIMPLE_UI_STEPS 5 - -// don't blink mid-ramp -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - -// thermal config - -// temperature limit -#define THERM_FASTER_LEVEL 130 // stop panicking at 50%/3A -#define MIN_THERM_STEPDOWN MAX_1x7135 - -//#define THERM_LOOKAHEAD 4 // 4 by default -> decrease for longer turbo -#define THERM_NEXT_WARNING_THRESHOLD 48 // 24 by default -> increase for fewer adjustments (more stable output on large time scale) -#define THERM_RESPONSE_MAGNITUDE 32 // 64 by default -> decrease for smaller adjustments (removes dip post turbo) -//#define THERM_WINDOW_SIZE 1 // 2 by default -> decrease for tighter temperature regulation - - -// UI - -//#define SIMPLE_UI_ACTIVE 0 // advanced UI by default - -// allow Aux Config and Strobe Modes in Simple UI -#define USE_EXTENDED_SIMPLE_UI - -// Allow 3C in Simple UI for switching between smooth and stepped ramping -#define USE_SIMPLE_UI_RAMPING_TOGGLE - -#define DEFAULT_2C_STYLE 1 // enable 2 click turbo - - -// AUX - -//#define USE_BUTTON_LED - -// this light has three aux LED channels: R, G, B -#define USE_AUX_RGB_LEDS - -// show each channel while it scroll by in the menu -#define USE_CONFIG_COLORS - -// blink numbers on the main LEDs by default -#define DEFAULT_BLINK_CHANNEL CM_MAIN - -// use aux red + aux blue for police strobe -#define USE_POLICE_COLOR_STROBE_MODE -#define POLICE_STROBE_USES_AUX -#define POLICE_COLOR_STROBE_CH1 CM_AUXRED -#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU - -// the aux LEDs are front-facing, so turn them off while main LEDs are on -#ifdef USE_INDICATOR_LED_WHILE_RAMPING -#undef USE_INDICATOR_LED_WHILE_RAMPING -#endif - - -// Misc - -#define PARTY_STROBE_ONTIME 1 // slow down party strobe -#define STROBE_OFF_LEVEL 1 // keep the regulator chip on between pulses - -// smoother candle mode with bigger oscillations -#define CANDLE_AMPLITUDE 40 - -// enable 13H factory reset so it can be used on tail e-switch lights -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/cfg-thefreeman-lin16dac.h b/spaghetti-monster/anduril/cfg-thefreeman-lin16dac.h deleted file mode 100644 index 20d2bf5..0000000 --- a/spaghetti-monster/anduril/cfg-thefreeman-lin16dac.h +++ /dev/null @@ -1,61 +0,0 @@ -// thefreeman's Linear 16 driver using DAC control -// Copyright (C) 2021-2023 (FIXME) -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "1630" -#include "hwdef-thefreeman-lin16dac.h" -// ATTINY: 1616 - -// the button lights up -#define USE_INDICATOR_LED -// the button is visible while main LEDs are on -#define USE_INDICATOR_LED_WHILE_RAMPING -// off mode: low (1) -// lockout: blinking (3) -#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) - -// level_calc.py ninth 2 150 7135 1 0.03 6.4 7135 1 6.3 1600 -#define RAMP_SIZE 150 - -// 4 ramp segments: -// - low 0.55V -// - low 2.5V -// - high 0.55V -// - high 2.5V -// PWM1: DAC Data -// FIXME: ramp stalls with 8 duplicate levels in a row -// (maybe use 1.1V Vref during that part of the ramp?) -#define PWM1_LEVELS 25, 25, 33, 41, 41, 50, 58, 66, 75, 83, 92,108,117,133,150,167,192,209,234, \ - 58, 64, 71, 80, 90, 99,110,121,134,149,163,180,198,218,241, \ - 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 10, 11, 11, 12, 13, 14, 15, 16, 18, 19, 20, 22, 23, 25, 26, 28, 30, 32, 34, 36, 39, 41, 44, 47, 50, 53, 56, 59, 63, 67, 71, 75, 79, 84, 89, 94,100,105,112,118,124,131,139,146,154,163,172,181,191,201,212,223,234,246, \ - 57, 60, 63, 66, 69, 73, 76, 80, 84, 88, 93, 97,102,107,112,117,123,129,135,141,147,154,161,169,176,184,193,201,210,220,229,239,250,255 -// PWM Tops: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20) -#define PWM_TOPS 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \ - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \ - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18 - -#define MAX_1x7135 34 -#define HDR_ENABLE_LEVEL_MIN 35 // bottom level of top half of the ramp -#define HALFSPEED_LEVEL 255 // always run at 1/4th speed -#define QUARTERSPEED_LEVEL 255 // because DAC doesn't use PWM - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 120 -// 10, 28, 46, [65], 83, 101, 120 -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 7 - -// stop panicking at ~30% power -#define THERM_FASTER_LEVEL 123 - -// enable 2 click turbo -#define DEFAULT_2C_STYLE 1 - -// don't blink mid-ramp -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - diff --git a/spaghetti-monster/anduril/cfg-wurkkos-fc13.h b/spaghetti-monster/anduril/cfg-wurkkos-fc13.h deleted file mode 100644 index 56161b4..0000000 --- a/spaghetti-monster/anduril/cfg-wurkkos-fc13.h +++ /dev/null @@ -1,17 +0,0 @@ -// Wurkkos FC13, like a TS25 but with a RGB button and no front aux -// Copyright (C) 2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// ATTINY: 1616 -#include "cfg-wurkkos-ts25.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0716" - -// this light has three aux LED channels: R, G, B -#define USE_AUX_RGB_LEDS - -// turn on the aux LEDs while main LEDs are on -#define USE_AUX_RGB_LEDS_WHILE_ON 20 -#define USE_INDICATOR_LED_WHILE_RAMPING - diff --git a/spaghetti-monster/anduril/cfg-wurkkos-ts10-rgbaux.h b/spaghetti-monster/anduril/cfg-wurkkos-ts10-rgbaux.h deleted file mode 100644 index 6770c47..0000000 --- a/spaghetti-monster/anduril/cfg-wurkkos-ts10-rgbaux.h +++ /dev/null @@ -1,85 +0,0 @@ -// Wurkkos TS10 (RGB aux version) config options for Anduril -// Copyright (C) 2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// most things are the same as TS25 -#include "cfg-wurkkos-ts25.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0713" -// ATTINY: 1616 - -///// apply some config from the TS10 ///// - -// use the TS10 ramp, not the TS25 ramp -#undef PWM1_LEVELS -#undef PWM2_LEVELS -#undef PWM_TOPS -#undef MAX_1x7135 -#undef MIN_THERM_STEPDOWN -#undef HALFSPEED_LEVEL -#undef QUARTERSPEED_LEVEL -#undef DEFAULT_LEVEL -#undef RAMP_SMOOTH_FLOOR -#undef RAMP_SMOOTH_CEIL -#undef RAMP_DISCRETE_FLOOR -#undef RAMP_DISCRETE_CEIL -#undef RAMP_DISCRETE_STEPS -#undef SIMPLE_UI_FLOOR -#undef SIMPLE_UI_CEIL -#undef SIMPLE_UI_STEPS -#undef THERM_FASTER_LEVEL - -// 7135 at 90/150 -// level_calc.py 5.7895 2 150 7135 0 0.1 125.25 FET 1 10 1200 --pwm dyn:61:4096:255:2.5 --clock 5:11:2.0 -// (with heavy manual tweaks up to ~15/150) -#define PWM1_LEVELS 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 23, 24, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 35, 36, 37, 38, 40, 41, 43, 45, 47, 50, 53, 56, 60, 63, 67, 71, 75, 79, 84, 89, 94, 99,104,110,116,122,129,136,143,150,158,166,174,183,192,202,211,222,232,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 5, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 29, 31, 34, 37, 39, 42, 45, 48, 51, 54, 58, 61, 65, 68, 72, 76, 80, 84, 88, 93, 97,102,107,112,117,122,127,133,139,145,151,157,163,170,177,183,191,198,205,213,221,229,238,246,255 -#define PWM_TOPS 4095,2893,3917,2806,3252,2703,2684,2660,2640,2370,3000,2900,2630,2549,2246,2193,2030,1961,1889,1716,1642,1569,1497,1428,1290,1232,1176,1122,1070,976,932,890,849,779,745,685,656,605,579,536,514,476,457,424,407,379,364,340,327,314,302,291,280,276,266,262,257,253,253,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#define MAX_1x7135 90 -#define MIN_THERM_STEPDOWN 60 -#define HALFSPEED_LEVEL 11 -#define QUARTERSPEED_LEVEL 5 -#define DEFAULT_LEVEL 50 - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 130 -// 20 38 56 [75] 93 111 130 -// 10 30 50 70 [90] 110 130 -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL 130 -#define RAMP_DISCRETE_STEPS 7 - -// at Wurkkos's request, reduce the Simple UI ceiling a little bit -// (i.e. not 150; original config had it at 144/150, or DD FET 204/255) -// 20 47 [75] 102 130 -// 10 30 50 70 [90] 110 130 -#define SIMPLE_UI_FLOOR 10 -#define SIMPLE_UI_CEIL 130 -#define SIMPLE_UI_STEPS 7 - -// stop panicking at ~50% power -#define THERM_FASTER_LEVEL 130 // throttle back faster when high - -// show each channel while it scroll by in the menu -#define USE_CONFIG_COLORS - -// blink numbers on the aux LEDs by default -#undef DEFAULT_BLINK_CHANNEL -#define DEFAULT_BLINK_CHANNEL CM_AUXWHT - -// the aux LEDs are pretty bright; set the high-mode threshold a bit higher -// (default is 15) -#define POST_OFF_VOLTAGE_BRIGHTNESS 25 - -// the default of 26 looks a bit rough, so increase it to make it smoother -#define CANDLE_AMPLITUDE 33 - -// don't blink mid-ramp -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - -// enable factory reset on 13H without loosening tailcap (required) -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/cfg-wurkkos-ts10.h b/spaghetti-monster/anduril/cfg-wurkkos-ts10.h deleted file mode 100644 index 90839e7..0000000 --- a/spaghetti-monster/anduril/cfg-wurkkos-ts10.h +++ /dev/null @@ -1,85 +0,0 @@ -// Wurkkos TS10 (originally used Sofirn SP36-t1616 firmware) config options for Anduril using the Attiny1616 -// Copyright (C) 2022-2023 (FIXME) -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// same as the BLF Q8 T1616, mostly (added Dynamic PWM) -#define MODEL_NUMBER "0714" -#include "hwdef-wurkkos-ts10.h" -#include "wurkkos-cfg.h" -// ATTINY: 1616 - -// uses forward-facing aux LEDs -#define USE_INDICATOR_LED -// don't turn on the aux LEDs while main LEDs are on -#ifdef USE_INDICATOR_LED_WHILE_RAMPING -#undef USE_INDICATOR_LED_WHILE_RAMPING -#endif -// the high button LED mode on this light uses too much power, default to low -// off mode: low (1) -// lockout: blinking (3) -#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) - -// voltage readings were a little high with the Q8 value -#undef VOLTAGE_FUDGE_FACTOR -#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V, not 0.35V - -#define RAMP_SIZE 150 - -#if 0 // 2022 version by SammysHP, smooth but only level 1/150 is underclocked -// level 1 by hand, for the rest -// level_calc.py 7.01 2 149 7135 3 0.5 125 FET 1 10 1200 --pwm dyn:63:2048:255 -#define PWM1_LEVELS 1,3,3,4,5,6,7,8,9,10,12,13,14,16,17,19,20,22,24,25,27,29,31,33,35,37,40,42,44,47,49,52,54,57,59,62,64,67,70,72,75,77,80,82,85,87,89,91,93,95,96,98,99,100,100,101,100,100,99,97,95,93,90,86,82,87,91,96,100,106,111,116,122,128,134,141,147,155,162,169,177,186,194,203,213,222,232,243,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,6,7,9,11,12,14,16,18,20,22,24,27,29,31,34,37,39,42,45,48,51,54,57,61,64,68,72,75,79,83,88,92,97,101,106,111,116,121,126,132,138,144,150,156,162,169,176,183,190,197,205,213,221,229,237,246,255 -#define PWM_TOPS 2047,2047,1198,1322,1584,1676,1701,1691,1662,1622,1774,1703,1631,1692,1613,1639,1558,1564,1559,1478,1464,1444,1420,1392,1361,1329,1331,1293,1256,1246,1207,1192,1152,1133,1094,1074,1035,1013,991,954,932,897,875,842,820,790,760,731,704,678,646,622,593,566,534,510,478,452,423,393,364,338,310,280,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#define MAX_1x7135 90 -#define HALFSPEED_LEVEL 2 -#define QUARTERSPEED_LEVEL 2 -#endif - -// 7135 at 90/150 -// level_calc.py 5.7895 2 150 7135 0 0.1 125.25 FET 1 10 1200 --pwm dyn:61:4096:255:2.5 --clock 5:11:2.0 -// (with heavy manual tweaks up to ~15/150) -#define PWM1_LEVELS 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 23, 24, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 35, 36, 37, 38, 40, 41, 43, 45, 47, 50, 53, 56, 60, 63, 67, 71, 75, 79, 84, 89, 94, 99,104,110,116,122,129,136,143,150,158,166,174,183,192,202,211,222,232,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 5, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 29, 31, 34, 37, 39, 42, 45, 48, 51, 54, 58, 61, 65, 68, 72, 76, 80, 84, 88, 93, 97,102,107,112,117,122,127,133,139,145,151,157,163,170,177,183,191,198,205,213,221,229,238,246,255 -#define PWM_TOPS 4095,2893,3917,2806,3252,2703,2684,2660,2640,2370,3000,2900,2630,2549,2246,2193,2030,1961,1889,1716,1642,1569,1497,1428,1290,1232,1176,1122,1070,976,932,890,849,779,745,685,656,605,579,536,514,476,457,424,407,379,364,340,327,314,302,291,280,276,266,262,257,253,253,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#define MAX_1x7135 90 -#define MIN_THERM_STEPDOWN 60 -#define HALFSPEED_LEVEL 11 -#define QUARTERSPEED_LEVEL 5 -#define DEFAULT_LEVEL 50 - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 130 -// 10 30 50 70 [90] 110 130 -#define RAMP_DISCRETE_FLOOR 10 -#define RAMP_DISCRETE_CEIL 130 -#define RAMP_DISCRETE_STEPS 7 - -// at Wurkkos's request, reduce the Simple UI ceiling a little bit -// (i.e. not 150; original config had it at 144/150, or DD FET 204/255) -// 10 30 50 70 [90] 110 130 -#define SIMPLE_UI_FLOOR 10 -#define SIMPLE_UI_CEIL 130 -#define SIMPLE_UI_STEPS 7 - -// stop panicking at ~50% power -#define THERM_FASTER_LEVEL 130 // throttle back faster when high - -// show each channel while it scroll by in the menu -#define USE_CONFIG_COLORS - -// blink numbers on the aux LEDs by default -#define DEFAULT_BLINK_CHANNEL CM_AUX - -// the default of 26 looks a bit rough, so increase it to make it smoother -#define CANDLE_AMPLITUDE 33 - -// don't blink mid-ramp -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - -// enable factory reset on 13H without loosening tailcap (required) -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/cfg-wurkkos-ts11.h b/spaghetti-monster/anduril/cfg-wurkkos-ts11.h deleted file mode 100644 index 8955731..0000000 --- a/spaghetti-monster/anduril/cfg-wurkkos-ts11.h +++ /dev/null @@ -1,19 +0,0 @@ -// Wurkkos TS11, like a TS25 but with a RGB button and RGB front aux -// Copyright (C) 2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// ATTINY: 1616 -#include "cfg-wurkkos-ts25.h" -#undef MODEL_NUMBER -#define MODEL_NUMBER "0717" - -// this light has three aux LED channels: R, G, B -#define USE_AUX_RGB_LEDS - -// turn on the aux LEDs while main LEDs are on -// (but not until the main LEDs are bright enough to overpower the aux) -// (setting this lower makes an annoying effect on some levels) -#define USE_AUX_RGB_LEDS_WHILE_ON 50 -#define USE_INDICATOR_LED_WHILE_RAMPING - diff --git a/spaghetti-monster/anduril/cfg-wurkkos-ts25.h b/spaghetti-monster/anduril/cfg-wurkkos-ts25.h deleted file mode 100644 index 852e91b..0000000 --- a/spaghetti-monster/anduril/cfg-wurkkos-ts25.h +++ /dev/null @@ -1,87 +0,0 @@ -// Wurkkos TS25, modelled after the TS10 but with RGB Aux -// Copyright (C) 2022-2023 (FIXME) -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define MODEL_NUMBER "0715" -#include "hwdef-wurkkos-ts25.h" -#include "wurkkos-cfg.h" -// ATTINY: 1616 - -// this light has three aux LED channels: R, G, B -#define USE_AUX_RGB_LEDS - -// don't turn on the aux LEDs while main LEDs are on -#ifdef USE_INDICATOR_LED_WHILE_RAMPING -#undef USE_INDICATOR_LED_WHILE_RAMPING -#endif - -// voltage readings were a little high with the Q8 value -#undef VOLTAGE_FUDGE_FACTOR -#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V, not 0.35V - -#define RAMP_SIZE 150 - -#if 0 // 2022 version -// level 1 by hand, for the rest -// level_calc.py 7.01 2 149 7135 3 0.5 125 FET 1 10 1200 --pwm dyn:63:2048:255 -#define PWM1_LEVELS 1,3,3,4,5,6,7,8,9,10,12,13,14,16,17,19,20,22,24,25,27,29,31,33,35,37,40,42,44,47,49,52,54,57,59,62,64,67,70,72,75,77,80,82,85,87,89,91,93,95,96,98,99,100,100,101,100,100,99,97,95,93,90,86,82,87,91,96,100,106,111,116,122,128,134,141,147,155,162,169,177,186,194,203,213,222,232,243,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,6,7,9,11,12,14,16,18,20,22,24,27,29,31,34,37,39,42,45,48,51,54,57,61,64,68,72,75,79,83,88,92,97,101,106,111,116,121,126,132,138,144,150,156,162,169,176,183,190,197,205,213,221,229,237,246,255 -#define PWM_TOPS 2047,2047,1198,1322,1584,1676,1701,1691,1662,1622,1774,1703,1631,1692,1613,1639,1558,1564,1559,1478,1464,1444,1420,1392,1361,1329,1331,1293,1256,1246,1207,1192,1152,1133,1094,1074,1035,1013,991,954,932,897,875,842,820,790,760,731,704,678,646,622,593,566,534,510,478,452,423,393,364,338,310,280,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 -#define MAX_1x7135 90 -#define HALFSPEED_LEVEL 2 -#define QUARTERSPEED_LEVEL 2 -#endif - -// 7135 at 75/150 -// level_calc.py 5.7895 2 150 7135 1 0.1 130 FET 1 10 3000 --pwm dyn:74:4096:255:3 -// (with some manual tweaks) -#define PWM1_LEVELS 1,1,2,3,3,4,5,6,7,8,9,11,12,13,15,16,18,19,21,23,26,27,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,71,74,76,78,80,82,85,87,90,93,96,100,103,107,112,116,122,127,133,140,147,154,163,171,182,192,203,215,228,241,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -// non-zero part of FET channel calculated with: -// level_calc.py 3 1 75 7135 1 200 3000 -// (FIXME? there's a visible bump when the FET kicks in, even with just 1/255) -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,7,8,10,11,13,14,16,17,19,21,22,24,26,28,30,32,34,37,39,41,44,46,48,51,54,56,59,62,65,68,71,74,77,81,84,87,91,94,98,102,106,110,114,118,122,126,130,135,139,144,148,153,158,163,168,173,178,184,189,195,200,206,212,218,224,230,236,242,248,255 -#define PWM_TOPS 4095,2701,3200,3586,2518,2778,2834,2795,2705,2587,2455,2582,2412,2247,2256,2091,2062,1907,1860,1802,1737,1605,1542,1477,1412,1347,1284,1222,1162,1105,1050,997,946,898,853,810,768,730,693,658,625,594,564,536,503,485,462,439,418,398,384,366,353,340,327,319,307,298,292,284,280,273,269,266,263,260,258,256,256,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 - -#define MAX_1x7135 75 -#define DEFAULT_LEVEL 50 -#define MIN_THERM_STEPDOWN 60 -#define HALFSPEED_LEVEL 20 -#define QUARTERSPEED_LEVEL 5 - -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 150 -// 20 38 56 [75] 93 111 130 -#define RAMP_DISCRETE_FLOOR 20 -#define RAMP_DISCRETE_CEIL 130 -#define RAMP_DISCRETE_STEPS 7 - -// at Wurkkos's request, reduce the Simple UI ceiling a little bit -// 25 50 [75] 100 125 -#define SIMPLE_UI_FLOOR 25 -#define SIMPLE_UI_CEIL 125 -#define SIMPLE_UI_STEPS 5 - -// stop panicking at ~50% power -#define THERM_FASTER_LEVEL 120 // throttle back faster when high - -// show each channel while it scroll by in the menu -#define USE_CONFIG_COLORS - -// blink numbers on the main LEDs by default -// (so battcheck will be visible while charging) -#define DEFAULT_BLINK_CHANNEL CM_MAIN -// blink numbers on the aux LEDs by default -//#define DEFAULT_BLINK_CHANNEL CM_AUXWHT - -// use aux red + aux blue for police strobe -#define USE_POLICE_COLOR_STROBE_MODE -#define POLICE_STROBE_USES_AUX -#define POLICE_COLOR_STROBE_CH1 CM_AUXRED -#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU - -// don't blink mid-ramp -#ifdef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_MIDDLE -#endif - diff --git a/spaghetti-monster/anduril/channel-modes.c b/spaghetti-monster/anduril/channel-modes.c deleted file mode 100644 index b2fc8d1..0000000 --- a/spaghetti-monster/anduril/channel-modes.c +++ /dev/null @@ -1,237 +0,0 @@ -// channel-modes.c: Multi-channel functions for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "channel-modes.h" - -uint8_t channel_mode_state(Event event, uint16_t arg) { - #ifdef USE_CHANNEL_MODE_ARGS - static int8_t tint_ramp_direction = 1; - static uint8_t prev_tint = 0; - // don't activate auto-tint modes unless the user hits the edge - // and keeps pressing for a while - static uint8_t past_edge_counter = 0; - // bugfix: click-click-hold from off to strobes would invoke tint ramping - // in addition to changing state... so ignore any tint-ramp events which - // don't look like they were meant to be here - static uint8_t active = 0; - uint8_t tint = cfg.channel_mode_args[channel_mode]; - #endif - - // it's possible that a light may need 3H but not 3C, - // so try to detect if 3C is needed - #if NUM_CHANNEL_MODES > 1 - // 3 clicks: next channel mode - if (event == EV_3clicks) { - uint8_t next = channel_mode; - // go to next channel mode until we find one which is enabled - // (and don't do any infinite loops if the user disabled them all) - uint8_t count = 0; - do { - count ++; - next = (next + 1) % NUM_CHANNEL_MODES; - } while ((! channel_mode_enabled(next)) && count < NUM_CHANNEL_MODES); - //} while ((! channel_modes_enabled[next]) && count < NUM_CHANNEL_MODES); - - // undo change if infinite loop detected (redundant?) - //if (NUM_CHANNEL_MODES == count) next = channel_mode; - - // if mode hasn't changed, abort - if (channel_mode == next) - return EVENT_NOT_HANDLED; - - set_channel_mode(next); - - // remember after battery changes - cfg.channel_mode = channel_mode; - save_config(); - return EVENT_HANDLED; - } else - #endif // if NUM_CHANNEL_MODES > 1 - - #ifdef USE_CUSTOM_CHANNEL_3H_MODES - // defer to mode-specific function if defined - if (channel_3H_modes[channel_mode]) { - StatePtr tint_func = channel_3H_modes[channel_mode]; - uint8_t err = tint_func(event, arg); - if (EVENT_HANDLED == err) return EVENT_HANDLED; - // else let the default handler run - } - #endif - #ifdef USE_CHANNEL_MODE_ARGS - #ifndef DONT_USE_DEFAULT_CHANNEL_ARG_MODE - // click, click, hold: change the current channel's arg (like tint) - if (event == EV_click3_hold) { - ///// adjust value from 0 to 255 - // reset at beginning of movement - if (! arg) { - active = 1; // first frame means this is for us - past_edge_counter = 0; // doesn't start until user hits the edge - } - // ignore event if we weren't the ones who handled the first frame - if (! active) return EVENT_NOT_HANDLED; - - #ifdef USE_STEPPED_TINT_RAMPING - if ((tint_ramp_direction > 0 && tint < 255) || - (tint_ramp_direction < 0 && tint > 0)) { - // ramp slower in stepped mode - if (cfg.tint_ramp_style && (arg % HOLD_TIMEOUT != 0)) - return EVENT_HANDLED; - - const uint8_t step_size = (cfg.tint_ramp_style < 2) - ? 1 : 254 / (cfg.tint_ramp_style-1); - tint = nearest_tint_value( - tint + ((int16_t)step_size * tint_ramp_direction) - ); - } - #else // smooth tint ramping only - if ((tint_ramp_direction > 0) && (tint < 255)) { tint ++; } - else - if ((tint_ramp_direction < 0) && (tint > 0)) { tint --; } - #endif // ifdef USE_STEPPED_TINT_RAMPING - - // if tint change stalled, let user know we hit the edge - else if (prev_tint == tint) { - if (past_edge_counter == 0) blip(); - past_edge_counter = 1; - } - prev_tint = tint; - cfg.channel_mode_args[channel_mode] = tint; - set_level(actual_level); - return EVENT_HANDLED; - } - - // click, click, hold, release: reverse direction for next ramp - else if (event == EV_click3_hold_release) { - active = 0; // ignore next hold if it wasn't meant for us - // reverse - tint_ramp_direction = -tint_ramp_direction; - if (0 == tint) tint_ramp_direction = 1; - else if (255 == tint) tint_ramp_direction = -1; - // remember tint after battery change - cfg.channel_mode_args[channel_mode] = tint; - save_config(); - // bug?: for some reason, brightness can seemingly change - // from 1/150 to 2/150 without this next line... not sure why - set_level(actual_level); - return EVENT_HANDLED; - } - #endif // ifndef DONT_USE_DEFAULT_CHANNEL_ARG_MODE - #endif // ifdef USE_CHANNEL_MODE_ARGS - - #if defined(USE_SIMPLE_UI) - // remaining mappings aren't "simple", so stop here - if (cfg.simple_ui_active) { - return EVENT_NOT_HANDLED; - } - #endif - - #if NUM_CHANNEL_MODES > 1 - // channel toggle menu on ... 9H? - else if (event == EV_click9_hold) { - push_state(channel_mode_config_state, 0); - return EVENT_HANDLED; - } - #endif - - return EVENT_NOT_HANDLED; -} - - -#if NUM_CHANNEL_MODES > 1 -void channel_mode_config_save(uint8_t step, uint8_t value) { - // 1 menu item per channel mode, to enable or disable that mode - step --; // step is 1-based, channel modes are 0-based - if (value) channel_mode_enable(step); - else channel_mode_disable(step); -} - -uint8_t channel_mode_config_state(Event event, uint16_t arg) { - uint8_t ret; - // make config steps match channel modes - config_color_per_step = true; - // 1 menu item per channel mode, to enable or disable that mode - ret = config_state_base( - event, arg, - NUM_CHANNEL_MODES, - channel_mode_config_save - ); - // no other menu needs this - config_color_per_step = false; - return ret; -} -#endif - - -#if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING) -uint8_t nearest_tint_value(const int16_t target) { - // const symbols for more readable code, will be removed by the compiler - const uint8_t tint_min = 0; - const uint8_t tint_max = 255; - const uint8_t tint_range = tint_max - tint_min; - - // only equal mix of both channels - if (1 == cfg.tint_ramp_style) return (tint_min + tint_max) >> 1; - - if (target < tint_min) return tint_min; - if (target > tint_max) return tint_max; - if (0 == cfg.tint_ramp_style) return target; // smooth ramping - - const uint8_t step_size = tint_range / (cfg.tint_ramp_style-1); - - uint8_t tint_result = tint_min; - for (uint8_t i=0; i>1)) return tint_result; - } - return tint_result; -} -#endif - -#ifdef USE_CIRCULAR_TINT_3H -uint8_t circular_tint_3h(Event event, uint16_t arg) { - static int8_t tint_ramp_direction = 1; - // bugfix: click-click-hold from off to strobes would invoke tint ramping - // in addition to changing state... so ignore any tint-ramp events which - // don't look like they were meant to be here - static uint8_t active = 0; - uint8_t tint = cfg.channel_mode_args[channel_mode]; - - // click, click, hold: change the current channel's arg (like tint) - if (event == EV_click3_hold) { - ///// adjust value from 0 to 255 in a circle - // reset at beginning of movement - if (! arg) { - active = 1; // first frame means this is for us - } - // ignore event if we weren't the ones who handled the first frame - if (! active) return EVENT_NOT_HANDLED; - - // smooth tint ramping only - tint += tint_ramp_direction; - - cfg.channel_mode_args[channel_mode] = tint; - set_level(actual_level); - return EVENT_HANDLED; - } - - // click, click, hold, release: reverse direction for next ramp - else if (event == EV_click3_hold_release) { - active = 0; // ignore next hold if it wasn't meant for us - // reverse - tint_ramp_direction = -tint_ramp_direction; - // remember tint after battery change - save_config(); - // bug?: for some reason, brightness can seemingly change - // from 1/150 to 2/150 without this next line... not sure why - set_level(actual_level); - return EVENT_HANDLED; - } - - return EVENT_NOT_HANDLED; -} -#endif diff --git a/spaghetti-monster/anduril/channel-modes.h b/spaghetti-monster/anduril/channel-modes.h deleted file mode 100644 index b51721d..0000000 --- a/spaghetti-monster/anduril/channel-modes.h +++ /dev/null @@ -1,26 +0,0 @@ -// channel-modes.h: Multi-channel functions for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -// not actually a mode, more of a fallback under other modes -uint8_t channel_mode_state(Event event, uint16_t arg); - -#if NUM_CHANNEL_MODES > 1 -uint8_t channel_mode_config_state(Event event, uint16_t arg); -#endif - -#if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING) -// calculate the nearest tint value which would be valid at the moment -uint8_t nearest_tint_value(const int16_t target); -#endif - -#ifdef USE_CUSTOM_CHANNEL_3H_MODES -StatePtr channel_3H_modes[NUM_CHANNEL_MODES]; -#endif - -#ifdef USE_CIRCULAR_TINT_3H -uint8_t circular_tint_3h(Event event, uint16_t arg); -#endif - diff --git a/spaghetti-monster/anduril/config-default.h b/spaghetti-monster/anduril/config-default.h deleted file mode 100644 index 899bc4a..0000000 --- a/spaghetti-monster/anduril/config-default.h +++ /dev/null @@ -1,207 +0,0 @@ -// config-default.h: Default configuration for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -/* - * This file specifies the default settings for Anduril. - * - * These settings can be overridden per build target, in cfg-*.h files... - * ... but most are not. So changing one here will make it change in - * almost every build target. - * - * Some configurable settings are also in other *.h files. - */ - -/********* User-configurable options *********/ -// low voltage protection (also required for battery check mode) -#define USE_LVP - -// overheat protection -#define USE_THERMAL_REGULATION -#if (ATTINY==85) || (ATTINY==1634) -// sloppy temperature sensor needs bigger error margin -#define DEFAULT_THERM_CEIL 45 // try not to get hotter than this (in C) -#else -// more accurate temperature sensor can regulate higher safely -#define DEFAULT_THERM_CEIL 50 // try not to get hotter than this (in C) -#endif -// Comment out to disable automatic calibration on factory reset -// - If so, be sure to set THERM_CAL_OFFSET to the correct calibration offset -// - Calibration can still be overridden in temperature check mode -// Or uncomment to use the default auto-calibrate on factory reset -// -// To determine THERM_CAL_OFFSET, comment out USE_THERM_AUTOCALIBRATE to -// disable auto-calibration, compile and flash, let flashlight rest at a known -// temperature, then enter temp check mode (do NOT enter calibration mode). -// -// THERM_CAL_OFFSET = known_temperature - temp_check_blinks + THERM_CAL_OFFSET -// -// (include THERM_CAL_OFFSET in sum as it might already be a non-zero number) -#define USE_THERM_AUTOCALIBRATE - -// Include a simplified UI for non-enthusiasts? -#define USE_SIMPLE_UI - - -///// Ramp mode options ///// - -// button timing for turning light on/off: -// B_PRESS_T: activate as soon as button is pressed -// B_RELEASE_T: activate when user lets go of button -// B_TIMEOUT_T: activate when we're sure the user won't double-click -// defaults are release on, timeout off -#define B_TIMING_ON B_RELEASE_T -#define B_TIMING_OFF B_TIMEOUT_T - -// default ramp style: 0 = smooth, 1 = stepped -#define RAMP_STYLE 0 - -// smooth ramp speed: 1, 2, 3, 4, ... for 1X speed, 1/2, 1/3rd, 1/4th, ... -#define USE_RAMP_SPEED_CONFIG - -// after ramping, how long until the direction resets to "up"? -#define AUTO_REVERSE_TIME (TICKS_PER_SECOND * 2 / 3) - -// add runtime option for whether hold-from-off should ramp or stay at moon -#define USE_RAMP_AFTER_MOON_CONFIG - -// short blip when crossing from "click" to "hold" from off -// (helps the user hit moon mode exactly, instead of holding too long -// or too short) -#define MOON_TIMING_HINT // only applies if B_TIMING_ON == B_PRESS_T -// short blips while ramping -#define BLINK_AT_RAMP_MIDDLE -//#define BLINK_AT_RAMP_FLOOR -#define BLINK_AT_RAMP_CEIL -//#define BLINK_AT_STEPS // whenever a discrete ramp mode is passed in smooth mode - -// Uncomment for Anduril1 "Ramp 2C" behavior: -// - Ramp 2C goes to turbo (advanced UI) or ceiling (simple UI), like in Anduril1 -// Or comment out to use Anduril2 behavior instead: -// - Ramp 2C goes to ceiling, unless already at ceiling or in simple UI. -// (Advanced UI ceiling 2C goes to turbo) -//#define USE_2C_MAX_TURBO -// Or uncomment to let the user decide which style they want: -#define USE_2C_STYLE_CONFIG -// 0 = no turbo -// 1 = A1 style: Off 2C = ceil, On 2C = turbo -// 2 = A2 style: Off 2C = ceil, On 2C = ceil, Ramped ceil 2C = turbo -// All styles allow momentary turbo in advanced UI -//#define DEFAULT_2C_STYLE 2 // default to Anduril 2 style -//#define DEFAULT_2C_STYLE_SIMPLE 0 // no turbo at all - -// make the ramps configurable by the user -#define USE_RAMP_CONFIG - -// adds a runtime option to switch between automatic memory (default) -// and manual memory (only available if compiled in) -// (manual memory makes 1-click-from-off start at the same level each time) -// (the level can be set explicitly with 10 clicks from on, -// or the user can go back to automatic with 10H) -#define USE_MANUAL_MEMORY -// if enabled, user can use "hybrid memory" -// The light will use automatic or manual memory level, depending on how long -// the light was off. Short off = automatic, long off = manual. -// This also remaps 10C/10H: -// - 10C toggles manual mem on/off at current level. -// - 10H configures the timer value. -#define USE_MANUAL_MEMORY_TIMER - -// enable sunset timer (ramp-down and automatic shutoff) -// timer is available in regular ramp mode and candle mode -#define USE_SUNSET_TIMER - - -///// What to do when power is connected ///// -// factory reset function erases user's runtime configuration in eeprom -#define USE_FACTORY_RESET -//#define USE_SOFT_FACTORY_RESET // only needed on models which can't use hold-button-at-boot - -// dual-switch support (second switch is a tail clicky) -// (currently incompatible with factory reset) -//#define START_AT_MEMORIZED_LEVEL - - -///// extra modes (enable / disable / configure each mode) ///// - -// include a function to blink out the firmware version -#define USE_VERSION_CHECK - -// enable the battery check mode (shows remaining charge) (requires USE_LVP) -#define USE_BATTCHECK_MODE -// battery readout style (pick one) -// TODO: allow VpT and 4-bar simultaneously, -// so one can be in "simple mode" and the other in "advanced mode" -#define BATTCHECK_VpT -//#define BATTCHECK_8bars // FIXME: breaks build -//#define BATTCHECK_4bars // FIXME: breaks build -// allow the user to calibrate the voltage readings? -// (adjust in 0.05V increments from -0.30V to +0.30V) -// (1 = -0.30V, 2 = -0.25V, ... 7 = 0V, ... 13 = +0.30V) -#define USE_VOLTAGE_CORRECTION - -// enable beacon mode -#define USE_BEACON_MODE - -// enable/disable various strobe modes -#define USE_BIKE_FLASHER_MODE -#define USE_PARTY_STROBE_MODE -#define USE_TACTICAL_STROBE_MODE -#define USE_LIGHTNING_MODE -#define USE_CANDLE_MODE - -// boring strobes nobody really likes, but sometimes flashlight companies want -// (these replace the fun strobe group, -// so don't enable them at the same time as any of the above strobes) -//#define USE_POLICE_STROBE_MODE -#define USE_SOS_MODE -//#define USE_SOS_MODE_IN_FF_GROUP // put SOS in the "boring strobes" mode -#define USE_SOS_MODE_IN_BLINKY_GROUP // put SOS in the blinkies mode group - -// enable a mode for locking the light for safe carry -#define USE_LOCKOUT_MODE -// should lockout mode function as a momentary moon mode? -#define USE_MOON_DURING_LOCKOUT_MODE -// add an optional setting to lock the light after being off for a while -#define USE_AUTOLOCK - -// enable momentary mode -#define USE_MOMENTARY_MODE - -// enable tactical mode -#define USE_TACTICAL_MODE - - -// enable a shortcut for +10 in number entry mode -// (click for +1, hold for +10) -#define USE_NUMBER_ENTRY_PLUS10 - -// cut clock speed at very low modes for better efficiency -// (defined here so config files can override it) -#define USE_DYNAMIC_UNDERCLOCKING - -// if the aux LEDs oscillate between "full battery" and "empty battery" -// while in "voltage" mode, enable this to reduce the amplitude of -// those oscillations -#if (ATTINY==1616) || (ATTINY==1634) -#define USE_LOWPASS_WHILE_ASLEEP -#endif - -// if there's tint ramping, allow user to set it smooth or stepped -#define USE_STEPPED_TINT_RAMPING -#define DEFAULT_TINT_RAMP_STYLE 0 // smooth - -// Use "smooth steps" to soften on/off and step changes -// on MCUs with enough room for extra stuff like this -#if (ATTINY==1616) || (ATTINY==1634) -#define USE_SMOOTH_STEPS -#endif -// 0 = none, 1 = smooth, 2+ = undefined -#define DEFAULT_SMOOTH_STEPS_STYLE 1 - -// by default, allow user to set the channel for each strobe-group mode -// (but allow disabling this feature per build) -#define USE_CHANNEL_PER_STROBE - diff --git a/spaghetti-monster/anduril/config-mode.c b/spaghetti-monster/anduril/config-mode.c deleted file mode 100644 index 71b0d69..0000000 --- a/spaghetti-monster/anduril/config-mode.c +++ /dev/null @@ -1,196 +0,0 @@ -// config-mode.c: Config mode base functions for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "config-mode.h" - -// general helper function for config modes -uint8_t number_entry_state(Event event, uint16_t arg); -// return value from number_entry_state() -volatile uint8_t number_entry_value; - - -#if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1) -// TODO: promote this to fsm-channels.c ? -void set_chan_if(bool cond, uint8_t chan) { - if ((cond) && (chan != channel_mode)) - set_channel_mode(chan); -} -#endif - -// allow the user to set a new value for a config option -// can be called two ways: -// - with a "click" action: Configures first menu item only. -// - with a "hold" action: Sets user select a menu item and then -// choose a new value for it. User should hold button until light -// blinks N times, to choose menu item N. Then let go, and light -// should give a buzzing prompt to enter a number. Click N times -// at the prompt to set the new value to N. -// after completing this process, config state calls the savefunc callback -// and then returns to caller/parent state -uint8_t config_state_base( - Event event, - uint16_t arg, - uint8_t num_config_steps, - void (*savefunc)(uint8_t step, uint8_t value)) { - - static uint8_t config_step; - #ifdef USE_CONFIG_COLORS - static uint8_t orig_channel; - #endif - if (event == EV_enter_state) { - #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1) - orig_channel = channel_mode; - #endif - config_step = 0; - set_level(0); - // if button isn't held, configure first menu item - if (! button_last_state) { - config_step ++; - push_state(number_entry_state, 0); - } - } - - // if initial "hold" event still active - // blink while holding to indicate option number - #define B_CLICK_FLAGS (B_CLICK|B_HOLD|B_PRESS|B_RELEASE|B_TIMEOUT) - #define B_ANY_HOLD (B_CLICK|B_HOLD|B_PRESS) - #define B_ANY_HOLD_RELEASE (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT) - else if ((event & B_CLICK_FLAGS) == B_ANY_HOLD) { - if (config_step <= num_config_steps) { - #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1) - uint8_t chan = config_step - 1; - if (chan < NUM_CHANNEL_MODES) - set_chan_if(config_color_per_step, chan); - #endif - if ((TICKS_PER_SECOND/10) == (arg % (TICKS_PER_SECOND*3/2))) { - config_step ++; - // blink when config step advances - if (config_step <= num_config_steps) { - #ifdef CONFIG_BLINK_CHANNEL - set_chan_if(!config_color_per_step, CONFIG_BLINK_CHANNEL); - #endif - set_level(RAMP_SIZE * 3 / 8); - } - } - else { - // stay on at a low level to indicate menu is active - #ifdef CONFIG_WAITING_CHANNEL - set_chan_if(!config_color_per_step, CONFIG_WAITING_CHANNEL); - #endif - set_level(RAMP_SIZE * 1 / 8); - } - } else { - // turn off when end of menu is reached - set_level(0); - } - } - - // button release: activate number entry for one menu item - else if ((event & B_CLICK_FLAGS) == B_ANY_HOLD_RELEASE) { - // ask the user for a number, if they selected a menu item - if (config_step && config_step <= num_config_steps) { - #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1) - // put the colors back how they were - set_channel_mode(orig_channel); - #endif - push_state(number_entry_state, 0); - } - // exit after falling out of end of menu - else { - pop_state(); - } - } - - // an option was set (return from number_entry_state) - else if (event == EV_reenter_state) { - // process value with parent's callback - savefunc(config_step, number_entry_value); - // make changes persist in eeprom - save_config(); - pop_state(); - } - - #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1) - else if (event == EV_leave_state) { - // put the colors back how they were - set_channel_mode(orig_channel); - } - #endif - - // eat all other events; don't pass any through to parent - return EVENT_HANDLED; -} - -uint8_t number_entry_state(Event event, uint16_t arg) { - static uint8_t entry_step; - - if (event == EV_enter_state) { - number_entry_value = 0; - entry_step = 0; - set_level(0); // initial pause should be dark - } - - // advance through the process: - // 0: wait a moment - // 1: "buzz" while counting clicks - // 2: save and exit - else if (event == EV_tick) { - // wait a moment - if (entry_step == 0) { - if (arg > TICKS_PER_SECOND/2) { - entry_step ++; - empty_event_sequence(); // reset tick counter to 0 - } - } - // buzz while waiting for a number to be entered - else if (entry_step == 1) { - // time out and exit after 3 seconds - if (arg > TICKS_PER_SECOND*3) { - entry_step ++; - set_level(0); - } - // buzz for N seconds after last event - // (flicker every other frame, - // except first frame (so we can see flashes after each click)) - else if (arg) { - #ifdef CONFIG_WAITING_CHANNEL - set_chan_if(1, CONFIG_WAITING_CHANNEL); - #endif - set_level( (RAMP_SIZE/8) - + ((arg&2)<<2) ); - } - } - // all done, save result and return to parent state - else { - pop_state(); - } - return EVENT_HANDLED; - } - - // count clicks: click = +1, hold = +10 - else if ((event == EV_click1_release) - #ifdef USE_NUMBER_ENTRY_PLUS10 - || (event == EV_click1_hold_release) - #endif - ) { - entry_step = 1; // in case user clicked during initial delay - #ifdef USE_NUMBER_ENTRY_PLUS10 - if (event == EV_click1_hold_release) number_entry_value += 10; - else - #endif - number_entry_value ++; // update the result - empty_event_sequence(); // reset FSM's click count - #ifdef CONFIG_BLINK_CHANNEL - set_channel_mode(CONFIG_BLINK_CHANNEL); - #endif - set_level(RAMP_SIZE/2); // flash briefly - return EVENT_HANDLED; - } - - // eat all other events; don't pass any through to parent - return EVENT_HANDLED; -} - diff --git a/spaghetti-monster/anduril/config-mode.h b/spaghetti-monster/anduril/config-mode.h deleted file mode 100644 index d4a7652..0000000 --- a/spaghetti-monster/anduril/config-mode.h +++ /dev/null @@ -1,24 +0,0 @@ -// config-mode.h: Config mode base functions for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -// menus can use 2 colors -#if defined (CONFIG_WAITING_CHANNEL) || defined(CONFIG_BLINK_CHANNEL) -#define USE_CONFIG_COLORS -#endif - -#if NUM_CHANNEL_MODES > 1 -// when true, changes the channel mode for each config step -bool config_color_per_step = false; -#endif - -// config menu -uint8_t config_state_base( - Event event, - uint16_t arg, - uint8_t num_config_steps, - void (*savefunc)(uint8_t step, uint8_t value) - ); - diff --git a/spaghetti-monster/anduril/factory-reset-fsm.h b/spaghetti-monster/anduril/factory-reset-fsm.h deleted file mode 100644 index 3cb0875..0000000 --- a/spaghetti-monster/anduril/factory-reset-fsm.h +++ /dev/null @@ -1,10 +0,0 @@ -// factory-reset-fsm.h: FSM config options to enable factory reset in Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#ifdef USE_SOFT_FACTORY_RESET -#define USE_REBOOT -#endif - diff --git a/spaghetti-monster/anduril/factory-reset.c b/spaghetti-monster/anduril/factory-reset.c deleted file mode 100644 index f9fb472..0000000 --- a/spaghetti-monster/anduril/factory-reset.c +++ /dev/null @@ -1,73 +0,0 @@ -// factory-reset.c: Factory reset functions for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "factory-reset.h" - -// allows setting channel mode per animation stage, -// so it can ramp up in red then explode in white (as one example) - -void factory_reset() { - // display a warning for a few seconds before doing the actual reset, - // so the user has time to abort if they want - #define SPLODEY_TIME 2500 - #define SPLODEY_STEPS 64 - #define SPLODEY_TIME_PER_STEP (SPLODEY_TIME/SPLODEY_STEPS) - uint8_t bright; - uint8_t reset = 1; - // wind up to an explosion - #ifdef FACTORY_RESET_WARN_CHANNEL - set_channel_mode(FACTORY_RESET_WARN_CHANNEL); - #endif - for (bright=0; bright>1); - nice_delay_ms(SPLODEY_TIME_PER_STEP/2); - if (! button_is_pressed()) { - reset = 0; - break; - } - } - // explode, if button pressed long enough - if (reset) { - #if defined(FACTORY_RESET_WARN_CHANNEL) && defined(DEFAULT_CHANNEL_MODE) - // return to default channel before saving - set_channel_mode(DEFAULT_CHANNEL_MODE); - #endif - - // auto-calibrate temperature - // AVR 1-Series has factory calibrated thermal sensor, always remove the offset on reset - #if defined(USE_THERMAL_REGULATION) && defined(AVRXMEGA3) - // this will cancel out the offset - thermal_config_save(1, temperature - cfg.therm_cal_offset); - #elif defined(USE_THERMAL_REGULATION) && defined(USE_THERM_AUTOCALIBRATE) - // assume current temperature is 21 C - thermal_config_save(1, 21); - #endif - - // save all settings to eeprom - // (assuming they're all at default because we haven't loaded them yet) - save_config(); - - // explosion animation - #ifdef FACTORY_RESET_SUCCESS_CHANNEL - set_channel_mode(FACTORY_RESET_SUCCESS_CHANNEL); - #endif - bright = MAX_LEVEL; - for (; bright > 0; bright--) { - set_level(bright); - nice_delay_ms(SPLODEY_TIME_PER_STEP/8); - } - } - // explosion cancelled, fade away - else { - for (; bright > 0; bright--) { - set_level(bright); - nice_delay_ms(SPLODEY_TIME_PER_STEP/3); - } - } -} - diff --git a/spaghetti-monster/anduril/factory-reset.h b/spaghetti-monster/anduril/factory-reset.h deleted file mode 100644 index 63c25cd..0000000 --- a/spaghetti-monster/anduril/factory-reset.h +++ /dev/null @@ -1,8 +0,0 @@ -// factory-reset.h: Factory reset functions for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -void factory_reset(); - diff --git a/spaghetti-monster/anduril/ff-strobe-modes.c b/spaghetti-monster/anduril/ff-strobe-modes.c deleted file mode 100644 index b7a7303..0000000 --- a/spaghetti-monster/anduril/ff-strobe-modes.c +++ /dev/null @@ -1,62 +0,0 @@ -// ff-strobe-modes.c: Fireflies Flashlights strobe modes for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "ff-strobe-modes.h" - -uint8_t boring_strobe_state(Event event, uint16_t arg) { - // police strobe and SOS, meh - // 'st' reduces ROM size slightly - uint8_t st = boring_strobe_type; - - if (event == EV_enter_state) { - return EVENT_HANDLED; - } - // 1 click: off - else if (event == EV_1click) { - // reset to police strobe for next time - boring_strobe_type = 0; - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: rotate through strobe/flasher modes - else if (event == EV_2clicks) { - boring_strobe_type = (st + 1) % NUM_BORING_STROBES; - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - -inline void boring_strobe_state_iter() { - switch(boring_strobe_type) { - #ifdef USE_POLICE_STROBE_MODE - case 0: // police strobe - police_strobe_iter(); - break; - #endif - - #ifdef USE_SOS_MODE_IN_FF_GROUP - default: // SOS - sos_mode_iter(); - break; - #endif - } -} - -#ifdef USE_POLICE_STROBE_MODE -inline void police_strobe_iter() { - // one iteration of main loop() - // flash at 16 Hz then 8 Hz, 8 times each - for (uint8_t del=41; del<100; del+=41) { - for (uint8_t f=0; f<8; f++) { - set_level(STROBE_BRIGHTNESS); - nice_delay_ms(del >> 1); - set_level(0); - nice_delay_ms(del); - } - } -} -#endif - diff --git a/spaghetti-monster/anduril/ff-strobe-modes.h b/spaghetti-monster/anduril/ff-strobe-modes.h deleted file mode 100644 index d7adfec..0000000 --- a/spaghetti-monster/anduril/ff-strobe-modes.h +++ /dev/null @@ -1,15 +0,0 @@ -// ff-strobe-modes.h: Fireflies Flashlights strobe modes for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -uint8_t boring_strobe_state(Event event, uint16_t arg); -inline void boring_strobe_state_iter(); -uint8_t boring_strobe_type = 0; -void sos_blink(uint8_t num, uint8_t dah); -#ifdef USE_POLICE_STROBE_MODE -inline void police_strobe_iter(); -#endif -#define NUM_BORING_STROBES 2 - diff --git a/spaghetti-monster/anduril/hank-cfg.h b/spaghetti-monster/anduril/hank-cfg.h deleted file mode 100644 index 86ac605..0000000 --- a/spaghetti-monster/anduril/hank-cfg.h +++ /dev/null @@ -1,30 +0,0 @@ -// Intl-Outdoor (Hank)'s config options for Anduril -// Copyright (C) 2021-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -// config preferences for Hank Wang of Intl-Outdoor (Emisar, Noctigon) - -// RGB aux LEDs should use rainbow cycling mode -// to impress new customers -// (people usually change it to voltage mode later though) -#ifdef RGB_LED_OFF_DEFAULT -#undef RGB_LED_OFF_DEFAULT -#endif -#define RGB_LED_OFF_DEFAULT 0x18 // low, rainbow - -// half a second per color in rainbow mode -//#define RGB_RAINBOW_SPEED 0x03 - -// Allow 3C (or 6C) in Simple UI (toggle smooth or stepped ramping) -#define USE_SIMPLE_UI_RAMPING_TOGGLE - -// allow Aux Config and Strobe Modes in Simple UI -#define USE_EXTENDED_SIMPLE_UI - -// double click while on goes to full-power turbo, not ramp ceiling -#define DEFAULT_2C_STYLE 1 - -// for consistency with KR4 (not otherwise necessary though) -#define USE_SOFT_FACTORY_RESET - diff --git a/spaghetti-monster/anduril/load-save-config-fsm.h b/spaghetti-monster/anduril/load-save-config-fsm.h deleted file mode 100644 index d189d3a..0000000 --- a/spaghetti-monster/anduril/load-save-config-fsm.h +++ /dev/null @@ -1,139 +0,0 @@ -// load-save-config-fsm.h: FSM config for eeprom configuration in Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#define USE_EEPROM -// load into a custom RAM location instead of FSM's default byte array -#define EEPROM_OVERRIDE - -#ifdef USE_SIMPLE_UI -#define NUM_RAMPS 3 -#else -#define NUM_RAMPS 2 -#endif - -// let FSM know this config struct exists -#define USE_CFG - -typedef struct Config { - - ///// ramp vars - uint8_t ramp_style; - #ifdef USE_2C_STYLE_CONFIG - uint8_t ramp_2c_style; - #endif - #ifdef USE_RAMP_CONFIG - uint8_t ramp_floors[NUM_RAMPS]; - uint8_t ramp_ceils [NUM_RAMPS]; - uint8_t ramp_stepss[NUM_RAMPS]; - #endif - #ifdef USE_SIMPLE_UI - uint8_t simple_ui_active; - #ifdef USE_2C_STYLE_CONFIG - uint8_t ramp_2c_style_simple; - #endif - #endif - #ifdef USE_RAMP_AFTER_MOON_CONFIG - uint8_t dont_ramp_after_moon; - #endif - #ifdef USE_MANUAL_MEMORY - uint8_t manual_memory; - #ifdef USE_MANUAL_MEMORY_TIMER - uint8_t manual_memory_timer; - #endif - #endif - - ///// channel modes / color modes - #if NUM_CHANNEL_MODES > 1 - uint8_t channel_mode; - uint16_t channel_modes_enabled; - #ifdef USE_MANUAL_MEMORY - uint8_t manual_memory_channel_mode; - #endif - #ifdef DEFAULT_BLINK_CHANNEL - uint8_t blink_channel; - #endif - #endif - #ifdef USE_CHANNEL_MODE_ARGS - // this is an array, needs a few bytes - uint8_t channel_mode_args[NUM_CHANNEL_MODES]; - #ifdef USE_MANUAL_MEMORY - uint8_t manual_memory_channel_args[NUM_CHANNEL_MODES]; - #endif - #ifdef USE_STEPPED_TINT_RAMPING - uint8_t tint_ramp_style; - #endif - #endif - - ///// Smooth animation between steps, and for on/off - #ifdef USE_SMOOTH_STEPS - uint8_t smooth_steps_style; - #endif - - ///// strobe / blinky mode settings - #ifdef USE_STROBE_STATE - uint8_t strobe_type; - #if (NUM_CHANNEL_MODES > 1) && defined(USE_CHANNEL_PER_STROBE) - uint8_t strobe_channels[NUM_STROBES]; - #endif - #endif - #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) - uint8_t strobe_delays[2]; - #endif - #ifdef USE_BIKE_FLASHER_MODE - uint8_t bike_flasher_brightness; - #endif - #ifdef USE_BEACON_MODE - uint8_t beacon_seconds; - #endif - - ///// voltage and temperature - #ifdef USE_VOLTAGE_CORRECTION - uint8_t voltage_correction; - #endif - #ifdef USE_THERMAL_REGULATION - uint8_t therm_ceil; - int8_t therm_cal_offset; - #endif - - ///// aux LEDs - #ifdef USE_INDICATOR_LED - uint8_t indicator_led_mode; - #endif - #ifdef USE_AUX_RGB_LEDS - uint8_t rgb_led_off_mode; - uint8_t rgb_led_lockout_mode; - #ifdef USE_POST_OFF_VOLTAGE - uint8_t post_off_voltage; - #endif - #endif - - ///// misc other mode settings - #ifdef USE_AUTOLOCK - uint8_t autolock_time; - #endif - #ifdef USE_TACTICAL_MODE - uint8_t tactical_levels[3]; - #endif - - ///// hardware config / globals menu - #ifdef USE_JUMP_START - uint8_t jump_start_level; - #endif - -} Config; - -// auto-detect how many eeprom bytes -#define EEPROM_BYTES sizeof(Config) - -// declare this so FSM can see it, -// but define its values in a file which loads later -Config cfg; - -#ifdef START_AT_MEMORIZED_LEVEL -#define USE_EEPROM_WL -#define EEPROM_WL_BYTES 1 -#endif - diff --git a/spaghetti-monster/anduril/load-save-config.c b/spaghetti-monster/anduril/load-save-config.c deleted file mode 100644 index aa772e1..0000000 --- a/spaghetti-monster/anduril/load-save-config.c +++ /dev/null @@ -1,33 +0,0 @@ -// load-save-config.c: Load/save/eeprom functions for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "load-save-config-fsm.h" -#include "load-save-config.h" - -void load_config() { - eeprom = (uint8_t *)&cfg; - - if (! load_eeprom()) return; - - #ifdef START_AT_MEMORIZED_LEVEL - if (load_eeprom_wl()) { - memorized_level = eeprom_wl[0]; - } - #endif -} - -void save_config() { - eeprom = (uint8_t *)&cfg; - save_eeprom(); -} - -#ifdef START_AT_MEMORIZED_LEVEL -void save_config_wl() { - eeprom_wl[0] = memorized_level; - save_eeprom_wl(); -} -#endif - diff --git a/spaghetti-monster/anduril/load-save-config.h b/spaghetti-monster/anduril/load-save-config.h deleted file mode 100644 index 514fcbb..0000000 --- a/spaghetti-monster/anduril/load-save-config.h +++ /dev/null @@ -1,173 +0,0 @@ -// load-save-config.h: Load/save/eeprom functions for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -// remember stuff even after battery was changed -void load_config(); -void save_config(); -#ifdef START_AT_MEMORIZED_LEVEL -void save_config_wl(); -#endif - -// a struct to hold config values -Config cfg = { - - ///// ramp vars - - // smooth vs discrete ramping - .ramp_style = RAMP_STYLE, // 0 = smooth, 1 = discrete - #ifdef USE_2C_STYLE_CONFIG - // 1 = A1 style, 2 = A2 style - .ramp_2c_style = DEFAULT_2C_STYLE, - #endif - - // settings for each ramp (smooth, stepped, simple UI) - #ifdef USE_RAMP_CONFIG - .ramp_floors = { - RAMP_SMOOTH_FLOOR, - RAMP_DISCRETE_FLOOR, - #ifdef USE_SIMPLE_UI - SIMPLE_UI_FLOOR, - #endif - }, - .ramp_ceils = { - RAMP_SMOOTH_CEIL, - RAMP_DISCRETE_CEIL, - #ifdef USE_SIMPLE_UI - SIMPLE_UI_CEIL, - #endif - }, - .ramp_stepss = { - DEFAULT_RAMP_SPEED, - RAMP_DISCRETE_STEPS, - #ifdef USE_SIMPLE_UI - SIMPLE_UI_STEPS, - #endif - }, - #endif - - #ifdef USE_SIMPLE_UI - // whether to enable the simplified interface or not - .simple_ui_active = SIMPLE_UI_ACTIVE, - #ifdef USE_2C_STYLE_CONFIG - // 0 = no turbo, 1 = A1 style, 2 = A2 style - .ramp_2c_style_simple = DEFAULT_2C_STYLE_SIMPLE, - #endif - #endif - - #ifdef USE_RAMP_AFTER_MOON_CONFIG - .dont_ramp_after_moon = DEFAULT_DONT_RAMP_AFTER_MOON, - #endif - - #ifdef USE_MANUAL_MEMORY - .manual_memory = DEFAULT_MANUAL_MEMORY, - #ifdef USE_MANUAL_MEMORY_TIMER - .manual_memory_timer = DEFAULT_MANUAL_MEMORY_TIMER, - #endif - #endif - - ///// channel modes / color modes - - #if NUM_CHANNEL_MODES > 1 - // current multi-channel mode - .channel_mode = DEFAULT_CHANNEL_MODE, - // user can take unwanted modes out of the rotation (bitmask) - .channel_modes_enabled = CHANNEL_MODES_ENABLED, - #ifdef USE_MANUAL_MEMORY - // reset w/ manual memory - .manual_memory_channel_mode = DEFAULT_CHANNEL_MODE, - #endif - #ifdef DEFAULT_BLINK_CHANNEL - // blink numbers in a specific channel (user configurable) - .blink_channel = DEFAULT_BLINK_CHANNEL, - #endif - #endif - #ifdef USE_CHANNEL_MODE_ARGS - // one byte of extra data per channel mode, like for tint value - .channel_mode_args = { CHANNEL_MODE_ARGS }, - #ifdef USE_MANUAL_MEMORY - // remember and reset 1 extra parameter per channel mode (like tint) - .manual_memory_channel_args = { CHANNEL_MODE_ARGS }, - #endif - #ifdef USE_STEPPED_TINT_RAMPING - .tint_ramp_style = DEFAULT_TINT_RAMP_STYLE, - #endif - #endif - - ///// Smooth animation between steps, and for on/off - #ifdef USE_SMOOTH_STEPS - .smooth_steps_style = DEFAULT_SMOOTH_STEPS_STYLE, - #endif - - ///// strobe / blinky mode settings - - #ifdef USE_STROBE_STATE - .strobe_type = DEFAULT_STROBE, - #if (NUM_CHANNEL_MODES > 1) && defined(USE_CHANNEL_PER_STROBE) - // channel mode saved per strobe-group mode - #ifdef DEFAULT_STROBE_CHANNELS - .strobe_channels = { DEFAULT_STROBE_CHANNELS }, - #endif - #endif - #endif - #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) - // party / tactical strobe timing - // party strobe 24 Hz, tactical strobe 10 Hz - .strobe_delays = { 41, 67 }, - #endif - #ifdef USE_BIKE_FLASHER_MODE - .bike_flasher_brightness = MAX_1x7135, - #endif - #ifdef USE_BEACON_MODE - // beacon timing - .beacon_seconds = 2, - #endif - - ///// voltage and temperature - - #ifdef USE_VOLTAGE_CORRECTION - // same 0.05V units as fudge factor, - // but 7 is neutral, and the expected range is from 1 to 13 - .voltage_correction = 7, - #endif - #ifdef USE_THERMAL_REGULATION - .therm_ceil = DEFAULT_THERM_CEIL, - .therm_cal_offset = 0, - #endif - - ///// aux LEDs - - #ifdef USE_INDICATOR_LED - // bits 2-3 control lockout mode - // bits 0-1 control "off" mode - // modes are: 0=off, 1=low, 2=high, 3=blinking (if TICK_DURING_STANDBY enabled) - .indicator_led_mode = INDICATOR_LED_DEFAULT_MODE, - #endif - #ifdef USE_AUX_RGB_LEDS - .rgb_led_off_mode = RGB_LED_OFF_DEFAULT, - .rgb_led_lockout_mode = RGB_LED_LOCKOUT_DEFAULT, - #ifdef USE_POST_OFF_VOLTAGE - // display voltage readout for a while after turning off? - .post_off_voltage = DEFAULT_POST_OFF_VOLTAGE_SECONDS, - #endif - #endif - - ///// misc other mode settings - - #ifdef USE_AUTOLOCK - .autolock_time = DEFAULT_AUTOLOCK_TIME, - #endif - #ifdef USE_TACTICAL_MODE - .tactical_levels = { TACTICAL_LEVELS }, - #endif - - ///// hardware config / globals menu - - #ifdef USE_JUMP_START - .jump_start_level = DEFAULT_JUMP_START_LEVEL, - #endif - -}; - diff --git a/spaghetti-monster/anduril/lockout-mode-fsm.h b/spaghetti-monster/anduril/lockout-mode-fsm.h deleted file mode 100644 index ede251c..0000000 --- a/spaghetti-monster/anduril/lockout-mode-fsm.h +++ /dev/null @@ -1,11 +0,0 @@ -// lockout-mode-fsm.h: FSM config for lockout mode in Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -// autolock function requires the ability to measure time while "off" -#ifdef USE_AUTOLOCK -#define TICK_DURING_STANDBY -#endif - diff --git a/spaghetti-monster/anduril/lockout-mode.c b/spaghetti-monster/anduril/lockout-mode.c deleted file mode 100644 index 422d081..0000000 --- a/spaghetti-monster/anduril/lockout-mode.c +++ /dev/null @@ -1,219 +0,0 @@ -// lockout-mode.c: Lockout mode for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "lockout-mode.h" - -uint8_t lockout_state(Event event, uint16_t arg) { - #ifdef USE_MOON_DURING_LOCKOUT_MODE - // momentary(ish) moon mode during lockout - // button is being held - #ifdef USE_AUX_RGB_LEDS - // don't turn on during RGB aux LED configuration - if (event == EV_click7_hold) { set_level(0); } else - #endif - if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { - // hold: lowest floor - // click, hold: highest floor (or manual mem level) - uint8_t lvl = cfg.ramp_floors[0]; - if (1 == (event & 0x0f)) { // first click - if (cfg.ramp_floors[1] < lvl) lvl = cfg.ramp_floors[1]; - } else { // 2nd click or later - if (cfg.ramp_floors[1] > lvl) lvl = cfg.ramp_floors[1]; - #ifdef USE_MANUAL_MEMORY - if (cfg.manual_memory) lvl = cfg.manual_memory; - #endif - } - set_level(lvl); - } - // button was released - else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { - set_level(0); - } - #endif // ifdef USE_MOON_DURING_LOCKOUT_MODE - - // regular event handling - // conserve power while locked out - // (allow staying awake long enough to exit, but otherwise - // be persistent about going back to sleep every few seconds - // even if the user keeps pressing the button) - if (event == EV_enter_state) { - ticks_since_on = 0; - #ifdef USE_INDICATOR_LED - // redundant, sleep tick does the same thing - // indicator_led_update(cfg.indicator_led_mode >> 2, 0); - #elif defined(USE_AUX_RGB_LEDS) - rgb_led_update(cfg.rgb_led_lockout_mode, 0); - #endif - } - - else if (event == EV_tick) { - if (arg > HOLD_TIMEOUT) { - go_to_standby = 1; - #ifdef USE_INDICATOR_LED - // redundant, sleep tick does the same thing - //indicator_led_update(cfg.indicator_led_mode >> 2, arg); - #elif defined(USE_AUX_RGB_LEDS) - rgb_led_update(cfg.rgb_led_lockout_mode, arg); - #endif - } - return EVENT_HANDLED; - } - - #if defined(TICK_DURING_STANDBY) && (defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS)) - else if (event == EV_sleep_tick) { - if (ticks_since_on < 255) ticks_since_on ++; - #ifdef USE_MANUAL_MEMORY_TIMER - // reset to manual memory level when timer expires - if (cfg.manual_memory && - (arg >= (cfg.manual_memory_timer * SLEEP_TICKS_PER_MINUTE))) { - manual_memory_restore(); - } - #endif - #if defined(USE_INDICATOR_LED) - indicator_led_update(cfg.indicator_led_mode >> 2, arg); - #elif defined(USE_AUX_RGB_LEDS) - rgb_led_update(cfg.rgb_led_lockout_mode, arg); - #endif - return EVENT_HANDLED; - } - #endif - - // 3 clicks: exit and turn off - else if (event == EV_3clicks) { - blink_once(); - set_state(off_state, 0); - return EVENT_HANDLED; - } - - // 4 clicks: exit and turn on - else if (event == EV_4clicks) { - #if defined(USE_MANUAL_MEMORY) && !defined(USE_MANUAL_MEMORY_TIMER) - // this clause probably isn't used by any configs any more - // but is included just in case someone configures it this way - if (cfg.manual_memory) - set_state(steady_state, cfg.manual_memory); - else - #endif - set_state(steady_state, memorized_level); - return EVENT_HANDLED; - } - - // 4 clicks, but hold last: exit and start at floor - else if (event == EV_click4_hold) { - //blink_once(); - blip(); - // reset button sequence to avoid activating anything in ramp mode - current_event = 0; - // ... and back to ramp mode - set_state(steady_state, 1); - return EVENT_HANDLED; - } - - // 5 clicks: exit and turn on at ceiling level - else if (event == EV_5clicks) { - set_state(steady_state, MAX_LEVEL); - return EVENT_HANDLED; - } - - #if NUM_CHANNEL_MODES > 1 - // 3H: next channel mode - else if (event == EV_click3_hold) { - if (0 == (arg % TICKS_PER_SECOND)) { - // pretend the user clicked 3 times to change channels - return channel_mode_state(EV_3clicks, 0); - } - } - #endif - - ////////// Every action below here is blocked in the (non-Extended) Simple UI ////////// - - #if defined(USE_SIMPLE_UI) && !defined(USE_EXTENDED_SIMPLE_UI) - if (cfg.simple_ui_active) { - return EVENT_NOT_HANDLED; - } - #endif // if simple UI but not extended simple UI - - #if defined(USE_INDICATOR_LED) - // 7 clicks: rotate through indicator LED modes (lockout mode) - else if (event == EV_7clicks) { - #if defined(USE_INDICATOR_LED) - uint8_t mode = cfg.indicator_led_mode >> 2; - #ifdef TICK_DURING_STANDBY - mode = (mode + 1) & 3; - #else - mode = (mode + 1) % 3; - #endif - #ifdef INDICATOR_LED_SKIP_LOW - if (mode == 1) { mode ++; } - #endif - cfg.indicator_led_mode = (mode << 2) + (cfg.indicator_led_mode & 0x03); - // redundant, sleep tick does the same thing - //indicator_led_update(cfg.indicator_led_mode >> 2, arg); - #elif defined(USE_AUX_RGB_LEDS) - #endif - save_config(); - return EVENT_HANDLED; - } - #elif defined(USE_AUX_RGB_LEDS) - // 7 clicks: change RGB aux LED pattern - else if (event == EV_7clicks) { - uint8_t mode = (cfg.rgb_led_lockout_mode >> 4) + 1; - mode = mode % RGB_LED_NUM_PATTERNS; - cfg.rgb_led_lockout_mode = (mode << 4) | (cfg.rgb_led_lockout_mode & 0x0f); - rgb_led_update(cfg.rgb_led_lockout_mode, 0); - save_config(); - blink_once(); - return EVENT_HANDLED; - } - // 7H: change RGB aux LED color - else if (event == EV_click7_hold) { - setting_rgb_mode_now = 1; - if (0 == (arg & 0x3f)) { - uint8_t mode = (cfg.rgb_led_lockout_mode & 0x0f) + 1; - mode = mode % RGB_LED_NUM_COLORS; - cfg.rgb_led_lockout_mode = mode | (cfg.rgb_led_lockout_mode & 0xf0); - //save_config(); - } - rgb_led_update(cfg.rgb_led_lockout_mode, arg); - return EVENT_HANDLED; - } - // 7H, release: save new color - else if (event == EV_click7_hold_release) { - setting_rgb_mode_now = 0; - save_config(); - return EVENT_HANDLED; - } - #endif - - #if defined(USE_EXTENDED_SIMPLE_UI) && defined(USE_SIMPLE_UI) - ////////// Every action below here is blocked in the Extended Simple UI ////////// - if (cfg.simple_ui_active) { - return EVENT_NOT_HANDLED; - } - #endif // if extended simple UI - - #ifdef USE_AUTOLOCK - // 10H: configure the autolock option - else if (event == EV_click10_hold) { - push_state(autolock_config_state, 0); - return EVENT_HANDLED; - } - #endif - - return EVENT_NOT_HANDLED; -} - -#ifdef USE_AUTOLOCK -// set the auto-lock timer to N minutes, where N is the number of clicks -void autolock_config_save(uint8_t step, uint8_t value) { - cfg.autolock_time = value; -} - -uint8_t autolock_config_state(Event event, uint16_t arg) { - return config_state_base(event, arg, 1, autolock_config_save); -} -#endif // #ifdef USE_AUTOLOCK - diff --git a/spaghetti-monster/anduril/lockout-mode.h b/spaghetti-monster/anduril/lockout-mode.h deleted file mode 100644 index c2703a0..0000000 --- a/spaghetti-monster/anduril/lockout-mode.h +++ /dev/null @@ -1,16 +0,0 @@ -// lockout-mode.h: Lockout mode for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -// soft lockout -uint8_t lockout_state(Event event, uint16_t arg); - -#ifdef USE_AUTOLOCK -#ifndef DEFAULT_AUTOLOCK_TIME -#define DEFAULT_AUTOLOCK_TIME 0 // autolock time in minutes, 0 = disabled -#endif -uint8_t autolock_config_state(Event event, uint16_t arg); -#endif - diff --git a/spaghetti-monster/anduril/misc.c b/spaghetti-monster/anduril/misc.c deleted file mode 100644 index 1b92d6f..0000000 --- a/spaghetti-monster/anduril/misc.c +++ /dev/null @@ -1,42 +0,0 @@ -// misc.c: Misc extra functions for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "misc.h" - -/* no longer used -void blink_confirm(uint8_t num) { - uint8_t brightness = actual_level; - uint8_t bump = actual_level + BLINK_BRIGHTNESS; - if (bump > MAX_LEVEL) bump = 0; - for (; num>0; num--) { - set_level(bump); - delay_4ms(10/4); - set_level(brightness); - if (num > 1) { delay_4ms(100/4); } - } -} -*/ - -// make a short, visible pulse -// (either brighter or darker, depending on current brightness) -void blink_once() { - uint8_t brightness = actual_level; - uint8_t bump = brightness + BLINK_BRIGHTNESS; - if (bump > MAX_LEVEL) bump = 0; - - set_level(bump); - delay_4ms(BLINK_ONCE_TIME/4); - set_level(brightness); -} - -// Just go dark for a moment to indicate to user that something happened -void blip() { - uint8_t temp = actual_level; - set_level(0); - delay_4ms(3); - set_level(temp); -} - diff --git a/spaghetti-monster/anduril/misc.h b/spaghetti-monster/anduril/misc.h deleted file mode 100644 index 0f2992a..0000000 --- a/spaghetti-monster/anduril/misc.h +++ /dev/null @@ -1,10 +0,0 @@ -// misc.h: Misc extra functions for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -//void blink_confirm(uint8_t num); // no longer used -void blink_once(); -void blip(); - diff --git a/spaghetti-monster/anduril/models.py b/spaghetti-monster/anduril/models.py deleted file mode 100755 index 1985352..0000000 --- a/spaghetti-monster/anduril/models.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python - -import os -import re - -def main(args): - """models.py: scan build targets to generate the MODELS file - """ - - models = [] - - # load all cfg-*.h files - paths = os.listdir('.') - for p in paths: - if p.startswith('cfg-') and p.endswith('.h'): - m = load_cfg(p) - models.append(m) - - # sort by model number - foo = [(m.num, m.name, m) for m in models] - foo.sort() - models = [x[-1] for x in foo] - - fmt = '%s\t%-30s\t%s' - print(fmt % ('Model', 'Name', 'MCU')) - print(fmt % ('-----', '----', '---')) - for m in models: - print(fmt % (m.num, m.name, m.attiny)) - - print('\nDuplicates:') - for i, m in enumerate(models): - for m2 in models[i+1:]: - #if (m.num == m2.num) and (m is not m2): - if m.num == m2.num: - print('%s\t%s, %s' % (m.num, m.name, m2.name)) - - print('\nMissing:') - for m in models: - if not m.num: - print(m.name) - - -class Empty: - pass - - -def load_cfg(path): - m = Empty() - m.name, m.num, m.attiny = '', '', 'attiny85' - - m.name = path.replace('cfg-', '').replace('.h', '') - - num_pat = re.compile(r'#define\s+MODEL_NUMBER\s+"(\d+)"') - mcu_pat = re.compile(r'ATTINY:\s+(\d+)') - # TODO? use C preprocessor to generate more complete file to scan - with open(path) as fp: - for line in fp: - found = num_pat.search(line) - if found: - m.num = found.group(1) - found = mcu_pat.search(line) - if found: - m.attiny = 'attiny' + found.group(1) - - return m - - -if __name__ == "__main__": - import sys - main(sys.argv[1:]) - diff --git a/spaghetti-monster/anduril/momentary-mode.c b/spaghetti-monster/anduril/momentary-mode.c deleted file mode 100644 index a765142..0000000 --- a/spaghetti-monster/anduril/momentary-mode.c +++ /dev/null @@ -1,67 +0,0 @@ -// momentary-mode.c: Momentary mode for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "momentary-mode.h" - -uint8_t momentary_state(Event event, uint16_t arg) { - // init strobe mode, if relevant - #ifdef USE_STROBE_STATE - if ((event == EV_enter_state) && (momentary_mode != 0)) { - strobe_state(event, arg); - } - #endif - - // light up when the button is pressed; go dark otherwise - // button is being held - if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { - momentary_active = 1; - // 0 = ramping, 1 = strobes - if (momentary_mode == 0) { - set_level(memorized_level); - } - return EVENT_HANDLED; - } - // button was released - else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { - momentary_active = 0; - set_level(0); - //go_to_standby = 1; // sleep while light is off - return EVENT_HANDLED; - } - - // Sleep, dammit! (but wait a few seconds first) - // (because standby mode uses such little power that it can interfere - // with exiting via tailcap loosen+tighten unless you leave power - // disconnected for several seconds, so we want to be awake when that - // happens to speed up the process) - else if (event == EV_tick) { - #ifdef USE_STROBE_STATE - if (momentary_active) { - // 0 = ramping, non-zero = strobes - if (momentary_mode != 0) { - return strobe_state(event, arg); - } - } - else { - #endif - if (arg > TICKS_PER_SECOND*5) { // sleep after 5 seconds - go_to_standby = 1; // sleep while light is off - // turn off lighted button - #ifdef USE_INDICATOR_LED - indicator_led(0); - #elif defined(USE_AUX_RGB_LEDS) - rgb_led_update(0, 0); - #endif - } - #ifdef USE_STROBE_STATE - } - #endif - return EVENT_HANDLED; - } - - return EVENT_NOT_HANDLED; -} - diff --git a/spaghetti-monster/anduril/momentary-mode.h b/spaghetti-monster/anduril/momentary-mode.h deleted file mode 100644 index d774801..0000000 --- a/spaghetti-monster/anduril/momentary-mode.h +++ /dev/null @@ -1,11 +0,0 @@ -// momentary-mode.h: Momentary mode for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -// momentary / signalling mode -uint8_t momentary_state(Event event, uint16_t arg); -uint8_t momentary_mode = 0; // 0 = ramping, 1 = strobe -uint8_t momentary_active = 0; // boolean, true if active *right now* - diff --git a/spaghetti-monster/anduril/off-mode.c b/spaghetti-monster/anduril/off-mode.c deleted file mode 100644 index 0a381b7..0000000 --- a/spaghetti-monster/anduril/off-mode.c +++ /dev/null @@ -1,384 +0,0 @@ -// off-mode.c: "Off" mode for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "off-mode.h" - -#ifdef USE_SUNSET_TIMER -#include "sunset-timer.h" -#endif - -// set level smooth maybe -void off_state_set_level(uint8_t level); - - -uint8_t off_state(Event event, uint16_t arg) { - - // turn emitter off when entering state - if (event == EV_enter_state) { - // turn off - off_state_set_level(0); - #ifdef USE_SMOOTH_STEPS - // don't go to sleep while animating - arg |= smooth_steps_in_progress; - #endif - ticks_since_on = 0; - #if NUM_CHANNEL_MODES > 1 - // reset to ramp mode's channel when light turns off - channel_mode = cfg.channel_mode; - #endif - #ifdef USE_INDICATOR_LED - // redundant, sleep tick does the same thing - //indicator_led_update(cfg.indicator_led_mode & 0x03, 0); - #elif defined(USE_AUX_RGB_LEDS) - // redundant, sleep tick does the same thing - //rgb_led_update(cfg.rgb_led_off_mode, 0); - #endif - #ifdef USE_SUNSET_TIMER - sunset_timer = 0; // needs a reset in case previous timer was aborted - #endif - // sleep while off (lower power use) - // (unless delay requested; give the ADC some time to catch up) - if (! arg) { go_to_standby = 1; } - return EVENT_HANDLED; - } - - // go back to sleep eventually if we got bumped but didn't leave "off" state - else if (event == EV_tick) { - if (arg > HOLD_TIMEOUT - #ifdef USE_SMOOTH_STEPS - && (! smooth_steps_in_progress) - #endif - ) { - go_to_standby = 1; - #ifdef USE_INDICATOR_LED - // redundant, sleep tick does the same thing - //indicator_led_update(cfg.indicator_led_mode & 0x03, arg); - #elif defined(USE_AUX_RGB_LEDS) - // redundant, sleep tick does the same thing - //rgb_led_update(cfg.rgb_led_off_mode, arg); - #endif - } - return EVENT_HANDLED; - } - - #if defined(TICK_DURING_STANDBY) - // blink the indicator LED, maybe - else if (event == EV_sleep_tick) { - if (ticks_since_on < 255) ticks_since_on ++; - #ifdef USE_MANUAL_MEMORY_TIMER - // reset to manual memory level when timer expires - if (cfg.manual_memory && - (arg >= (cfg.manual_memory_timer * SLEEP_TICKS_PER_MINUTE))) { - manual_memory_restore(); - } - #endif - #ifdef USE_INDICATOR_LED - indicator_led_update(cfg.indicator_led_mode & 0x03, arg); - #elif defined(USE_AUX_RGB_LEDS) - rgb_led_update(cfg.rgb_led_off_mode, arg); - #endif - - #ifdef USE_AUTOLOCK - // lock the light after being off for N minutes - uint16_t ticks = cfg.autolock_time * SLEEP_TICKS_PER_MINUTE; - if ((cfg.autolock_time > 0) && (arg > ticks)) { - set_state(lockout_state, 0); - } - #endif // ifdef USE_AUTOLOCK - return EVENT_HANDLED; - } - #endif - - #if (B_TIMING_ON == B_PRESS_T) - // hold (initially): go to lowest level (floor), but allow abort for regular click - else if (event == EV_click1_press) { - off_state_set_level(nearest_level(1)); - return EVENT_HANDLED; - } - #endif // B_TIMING_ON == B_PRESS_T - - // hold: go to lowest level - else if (event == EV_click1_hold) { - #if (B_TIMING_ON == B_PRESS_T) - #ifdef MOON_TIMING_HINT - if (arg == 0) { - // let the user know they can let go now to stay at moon - blip(); - } else - #endif - #else // B_RELEASE_T or B_TIMEOUT_T - off_state_set_level(nearest_level(1)); - #endif - #ifdef USE_RAMP_AFTER_MOON_CONFIG - if (cfg.dont_ramp_after_moon) { - return EVENT_HANDLED; - } - #endif - // don't start ramping immediately; - // give the user time to release at moon level - //if (arg >= HOLD_TIMEOUT) { // smaller - if (arg >= (!cfg.ramp_style) * HOLD_TIMEOUT) { // more consistent - set_state(steady_state, 1); - } - return EVENT_HANDLED; - } - - // hold, release quickly: go to lowest level (floor) - else if (event == EV_click1_hold_release) { - set_state(steady_state, 1); - return EVENT_HANDLED; - } - - #if (B_TIMING_ON != B_TIMEOUT_T) - // 1 click (before timeout): go to memorized level, but allow abort for double click - else if (event == EV_click1_release) { - #if defined(USE_MANUAL_MEMORY) && !defined(USE_MANUAL_MEMORY_TIMER) - // this clause probably isn't used by any configs any more - // but is included just in case someone configures it this way - if (cfg.manual_memory) { - manual_memory_restore(); - } - #endif - off_state_set_level(nearest_level(memorized_level)); - return EVENT_HANDLED; - } - #endif // if (B_TIMING_ON != B_TIMEOUT_T) - - // 1 click: regular mode - else if (event == EV_1click) { - #if (B_TIMING_ON != B_TIMEOUT_T) - set_state(steady_state, memorized_level); - #else - // FIXME: B_TIMEOUT_T breaks manual_memory and manual_memory_timer - // (need to duplicate manual mem logic here, probably) - set_state(steady_state, memorized_level); - #endif - return EVENT_HANDLED; - } - - // click, hold: momentary at ceiling or turbo - else if (event == EV_click2_hold) { - ticks_since_on = 0; // momentary turbo is definitely "on" - uint8_t turbo_level; // how bright is "turbo"? - - #if defined(USE_2C_STYLE_CONFIG) // user can choose 2C behavior - uint8_t style_2c = cfg.ramp_2c_style; - #ifdef USE_SIMPLE_UI - // simple UI has its own turbo config - if (cfg.simple_ui_active) style_2c = cfg.ramp_2c_style_simple; - #endif - // 0 = ceiling - // 1+ = full power - if (0 == style_2c) turbo_level = nearest_level(MAX_LEVEL); - else turbo_level = MAX_LEVEL; - #else - // simple UI: ceiling - // full UI: full power - #ifdef USE_SIMPLE_UI - if (cfg.simple_ui_active) turbo_level = nearest_level(MAX_LEVEL); - else - #endif - turbo_level = MAX_LEVEL; - #endif - - off_state_set_level(turbo_level); - return EVENT_HANDLED; - } - else if (event == EV_click2_hold_release) { - off_state_set_level(0); - return EVENT_HANDLED; - } - - // 2 clicks: highest mode (ceiling) - else if (event == EV_2clicks) { - set_state(steady_state, MAX_LEVEL); - return EVENT_HANDLED; - } - - // 3 clicks (initial press): off, to prep for later events - else if (event == EV_click3_press) { - #ifdef USE_SMOOTH_STEPS - // immediately cancel any animations in progress - smooth_steps_in_progress = 0; - #endif - off_state_set_level(0); - return EVENT_HANDLED; - } - - #ifdef USE_BATTCHECK - // 3 clicks: battcheck mode / blinky mode group 1 - else if (event == EV_3clicks) { - set_state(battcheck_state, 0); - return EVENT_HANDLED; - } - #endif - - #ifdef USE_LOCKOUT_MODE - // 4 clicks: soft lockout - else if (event == EV_4clicks) { - blink_once(); - set_state(lockout_state, 0); - return EVENT_HANDLED; - } - #endif - - #if defined(USE_FACTORY_RESET) && defined(USE_SOFT_FACTORY_RESET) - // 13 clicks and hold the last click: invoke factory reset (reboot) - else if (event == EV_click13_hold) { - reboot(); - return EVENT_HANDLED; - } - #endif - - #ifdef USE_VERSION_CHECK - // 15+ clicks: show the version number - else if (event == EV_15clicks) { - set_state(version_check_state, 0); - return EVENT_HANDLED; - } - #endif - - #ifdef USE_SIMPLE_UI - // 10 clicks, but hold last click: turn simple UI off (or configure it) - else if ((event == EV_click10_hold) && (!arg)) { - if (cfg.simple_ui_active) { // turn off simple UI - blink_once(); - cfg.simple_ui_active = 0; - save_config(); - } - else { // configure simple UI ramp - push_state(simple_ui_config_state, 0); - } - return EVENT_HANDLED; - } - - ////////// Every action below here is blocked in the (non-Extended) Simple UI ////////// - - #ifndef USE_EXTENDED_SIMPLE_UI - if (cfg.simple_ui_active) { - return EVENT_NOT_HANDLED; - } - #endif // ifndef USE_EXTENDED_SIMPLE_UI - #endif // ifdef USE_SIMPLE_UI - - // click, click, long-click: strobe mode - #ifdef USE_STROBE_STATE - else if (event == EV_click3_hold) { - set_state(strobe_state, 0); - return EVENT_HANDLED; - } - #elif defined(USE_BORING_STROBE_STATE) - else if (event == EV_click3_hold) { - set_state(boring_strobe_state, 0); - return EVENT_HANDLED; - } - #endif - - #ifdef USE_INDICATOR_LED - // 7 clicks: change indicator LED mode - else if (event == EV_7clicks) { - uint8_t mode = (cfg.indicator_led_mode & 3) + 1; - #ifdef TICK_DURING_STANDBY - mode = mode & 3; - #else - mode = mode % 3; - #endif - #ifdef INDICATOR_LED_SKIP_LOW - if (mode == 1) { mode ++; } - #endif - cfg.indicator_led_mode = (cfg.indicator_led_mode & 0b11111100) | mode; - // redundant, sleep tick does the same thing - //indicator_led_update(cfg.indicator_led_mode & 0x03, arg); - save_config(); - return EVENT_HANDLED; - } - #elif defined(USE_AUX_RGB_LEDS) - // 7 clicks: change RGB aux LED pattern - else if (event == EV_7clicks) { - uint8_t mode = (cfg.rgb_led_off_mode >> 4) + 1; - mode = mode % RGB_LED_NUM_PATTERNS; - cfg.rgb_led_off_mode = (mode << 4) | (cfg.rgb_led_off_mode & 0x0f); - rgb_led_update(cfg.rgb_led_off_mode, 0); - save_config(); - blink_once(); - return EVENT_HANDLED; - } - // 7 clicks (hold last): change RGB aux LED color - else if (event == EV_click7_hold) { - setting_rgb_mode_now = 1; - if (0 == (arg & 0x3f)) { - uint8_t mode = (cfg.rgb_led_off_mode & 0x0f) + 1; - mode = mode % RGB_LED_NUM_COLORS; - cfg.rgb_led_off_mode = mode | (cfg.rgb_led_off_mode & 0xf0); - //save_config(); - } - rgb_led_update(cfg.rgb_led_off_mode, arg); - return EVENT_HANDLED; - } - else if (event == EV_click7_hold_release) { - setting_rgb_mode_now = 0; - save_config(); - return EVENT_HANDLED; - } - #endif // end 7 clicks - - ////////// Every action below here is blocked in the Extended Simple UI ////////// - - #ifdef USE_SIMPLE_UI - #ifdef USE_EXTENDED_SIMPLE_UI - if (cfg.simple_ui_active) { - return EVENT_NOT_HANDLED; - } - #endif // ifdef USE_EXTENDED_SIMPLE_UI - - // 10 clicks: enable simple UI - else if (event == EV_10clicks) { - blink_once(); - cfg.simple_ui_active = 1; - save_config(); - return EVENT_HANDLED; - } - #endif // ifdef USE_SIMPLE_UI - - #ifdef USE_MOMENTARY_MODE - // 5 clicks: momentary mode - else if (event == EV_5clicks) { - blink_once(); - set_state(momentary_state, 0); - return EVENT_HANDLED; - } - #endif - - #ifdef USE_TACTICAL_MODE - // 6 clicks: tactical mode - else if (event == EV_6clicks) { - blink_once(); - set_state(tactical_state, 0); - return EVENT_HANDLED; - } - #endif - - #ifdef USE_GLOBALS_CONFIG - // 9 clicks, but hold last click: configure misc global settings - else if ((event == EV_click9_hold) && (!arg)) { - push_state(globals_config_state, 0); - return EVENT_HANDLED; - } - #endif - - return EVENT_NOT_HANDLED; -} - - -void off_state_set_level(uint8_t level) { - // this pattern gets used a few times, so reduce duplication - #ifdef USE_SMOOTH_STEPS - if (cfg.smooth_steps_style) set_level_smooth(level, 8); - else - #endif - set_level(level); -} - diff --git a/spaghetti-monster/anduril/off-mode.h b/spaghetti-monster/anduril/off-mode.h deleted file mode 100644 index d07fff1..0000000 --- a/spaghetti-monster/anduril/off-mode.h +++ /dev/null @@ -1,12 +0,0 @@ -// off-mode.h: "Off" mode for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -// was the light in an "on" mode within the past second or so? -uint8_t ticks_since_on = 0; - -// when the light is "off" or in standby -uint8_t off_state(Event event, uint16_t arg); - diff --git a/spaghetti-monster/anduril/ramp-mode-fsm.h b/spaghetti-monster/anduril/ramp-mode-fsm.h deleted file mode 100644 index edfd6db..0000000 --- a/spaghetti-monster/anduril/ramp-mode-fsm.h +++ /dev/null @@ -1,38 +0,0 @@ -// ramp-mode-fsm.h: FSM config for ramping functions in Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -// enable FSM's ramping features -#define USE_RAMPING - -// do smooth adjustments when compensating for temperature -#ifdef USE_THERMAL_REGULATION -#define USE_SET_LEVEL_GRADUALLY // isn't used except for thermal adjustments -#endif - -// brightness to use when no memory is set -// FIXME: this is only here to stop an error in fsm-ramping.c, -// which should be fixed by using a different symbol instead -// (like BUTTON_LED_BRIGHT_LEVEL or RAMP_HALFWAY_LEVEL or something) -#ifndef DEFAULT_LEVEL -#define DEFAULT_LEVEL MAX_1x7135 -#endif - -// requires the ability to measure time while "off" -#ifdef USE_MANUAL_MEMORY_TIMER -#define TICK_DURING_STANDBY -#endif - -// ensure the jump start feature gets compiled in if needed -#ifdef DEFAULT_JUMP_START_LEVEL -#define USE_JUMP_START -#endif - -// include an extra config mode for random stuff which doesn't fit elsewhere -#if defined(USE_JUMP_START) || \ - (defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING)) -#define USE_GLOBALS_CONFIG -#endif - diff --git a/spaghetti-monster/anduril/ramp-mode.c b/spaghetti-monster/anduril/ramp-mode.c deleted file mode 100644 index 4611b4f..0000000 --- a/spaghetti-monster/anduril/ramp-mode.c +++ /dev/null @@ -1,741 +0,0 @@ -// ramp-mode.c: Ramping functions for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "ramp-mode.h" - -#ifdef USE_SUNSET_TIMER -#include "sunset-timer.h" -#endif - -#ifdef USE_SMOOTH_STEPS -#include "smooth-steps.h" -#endif - - -uint8_t steady_state(Event event, uint16_t arg) { - static int8_t ramp_direction = 1; - #if (B_TIMING_OFF == B_RELEASE_T) - // if the user double clicks, we need to abort turning off, - // and this stores the level to return to - static uint8_t level_before_off = 0; - #endif - - #if NUM_CHANNEL_MODES > 1 - channel_mode = cfg.channel_mode; - #endif - - // make sure ramp globals are correct... - // ... but they already are; no need to do it here - //ramp_update_config(); - //nearest_level(1); // same effect, takes less space - - uint8_t mode_min = ramp_floor; - uint8_t mode_max = ramp_ceil; - uint8_t step_size; - if (cfg.ramp_style) { step_size = ramp_discrete_step_size; } - else { step_size = 1; } - - // how bright is "turbo"? - uint8_t turbo_level; - #if defined(USE_2C_STYLE_CONFIG) // user can choose 2C behavior - uint8_t style_2c = cfg.ramp_2c_style; - #ifdef USE_SIMPLE_UI - // simple UI has its own turbo config - if (cfg.simple_ui_active) style_2c = cfg.ramp_2c_style_simple; - #endif - // 0 = no turbo - // 1 = Anduril 1 direct to turbo - // 2 = Anduril 2 direct to ceiling, or turbo if already at ceiling - if (0 == style_2c) turbo_level = mode_max; - else if (1 == style_2c) turbo_level = MAX_LEVEL; - else { - if (memorized_level < mode_max) { turbo_level = mode_max; } - else { turbo_level = MAX_LEVEL; } - } - #elif defined(USE_2C_MAX_TURBO) // Anduril 1 style always - // simple UI: to/from ceiling - // full UI: to/from turbo (Anduril1 behavior) - #ifdef USE_SIMPLE_UI - if (cfg.simple_ui_active) turbo_level = mode_max; - else - #endif - turbo_level = MAX_LEVEL; - #else // Anduril 2 style always - // simple UI: to/from ceiling - // full UI: to/from ceiling if mem < ceiling, - // or to/from turbo if mem >= ceiling - if ((memorized_level < mode_max) - #ifdef USE_SIMPLE_UI - || cfg.simple_ui_active - #endif - ) { turbo_level = mode_max; } - else { turbo_level = MAX_LEVEL; } - #endif - - #ifdef USE_SUNSET_TIMER - // handle the shutoff timer first - uint8_t sunset_active = sunset_timer; // save for comparison - // clock tick - sunset_timer_state(event, arg); - // if the timer was just turned on - if (sunset_timer && (! sunset_active)) { - sunset_timer_orig_level = actual_level; - } - // if the timer just expired, shut off - else if (sunset_active && (! sunset_timer)) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - #endif // ifdef USE_SUNSET_TIMER - - // turn LED on when we first enter the mode - if ((event == EV_enter_state) || (event == EV_reenter_state)) { - #if defined(USE_MOMENTARY_MODE) && defined(USE_STROBE_STATE) - momentary_mode = 0; // 0 = ramping, 1 = strobes - #endif - // if we just got back from config mode, go back to memorized level - if (event == EV_reenter_state) { - arg = memorized_level; - } - // remember this level, unless it's moon or turbo - if ((arg > mode_min) && (arg < mode_max)) - memorized_level = arg; - // use the requested level even if not memorized - arg = nearest_level(arg); - set_level_and_therm_target(arg); - ramp_direction = 1; - return EVENT_HANDLED; - } - #if (B_TIMING_OFF == B_RELEASE_T) - // 1 click (early): off, if configured for early response - else if (event == EV_click1_release) { - level_before_off = actual_level; - set_level_and_therm_target(0); - return EVENT_HANDLED; - } - // 2 clicks (early): abort turning off, if configured for early response - else if (event == EV_click2_press) { - set_level_and_therm_target(level_before_off); - return EVENT_HANDLED; - } - #endif // if (B_TIMING_OFF == B_RELEASE_T) - // 1 click: off - else if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: go to/from highest level - else if (event == EV_2clicks) { - if (actual_level < turbo_level) { - set_level_and_therm_target(turbo_level); - } - else { - set_level_and_therm_target(memorized_level); - } - #ifdef USE_SUNSET_TIMER - reset_sunset_timer(); - #endif - return EVENT_HANDLED; - } - - #ifdef USE_LOCKOUT_MODE - // 4 clicks: shortcut to lockout mode - else if (event == EV_4clicks) { - set_level(0); - set_state(lockout_state, 0); - return EVENT_HANDLED; - } - #endif - - // hold: change brightness (brighter, dimmer) - // click, hold: change brightness (dimmer) - else if ((event == EV_click1_hold) || (event == EV_click2_hold)) { - // ramp infrequently in stepped mode - if (cfg.ramp_style && (arg % HOLD_TIMEOUT != 0)) - return EVENT_HANDLED; - #ifdef USE_RAMP_SPEED_CONFIG - // ramp slower if user configured things that way - if ((! cfg.ramp_style) && (arg % ramp_speed)) - return EVENT_HANDLED; - #endif - #ifdef USE_SMOOTH_STEPS - // if a brightness transition is already happening, - // don't interrupt it - // (like 2C for full turbo then 1H to smooth ramp down - // ... without this clause, it flickers because it trips - // the "blink at ramp ceil" clause below, over and over) - if (smooth_steps_in_progress) return EVENT_HANDLED; - #endif - // fix ramp direction on first frame if necessary - if (!arg) { - // click, hold should always go down if possible - if (event == EV_click2_hold) { ramp_direction = -1; } - // make it ramp down instead, if already at max - else if (actual_level >= mode_max) { ramp_direction = -1; } - // make it ramp up if already at min - // (off->hold->stepped_min->release causes this state) - else if (actual_level <= mode_min) { ramp_direction = 1; } - } - // if the button is stuck, err on the side of safety and ramp down - else if ((arg > TICKS_PER_SECOND * 5 - #ifdef USE_RAMP_SPEED_CONFIG - // FIXME: count from time actual_level hits mode_max, - // not from beginning of button hold - * ramp_speed - #endif - ) && (actual_level >= mode_max)) { - ramp_direction = -1; - } - #ifdef USE_LOCKOUT_MODE - // if the button is still stuck, lock the light - else if ((arg > TICKS_PER_SECOND * 10 - #ifdef USE_RAMP_SPEED_CONFIG - // FIXME: count from time actual_level hits mode_min, - // not from beginning of button hold - * ramp_speed - #endif - ) && (actual_level <= mode_min)) { - blink_once(); - set_state(lockout_state, 0); - } - #endif - memorized_level = nearest_level((int16_t)actual_level \ - + (step_size * ramp_direction)); - #if defined(BLINK_AT_RAMP_CEIL) || defined(BLINK_AT_RAMP_MIDDLE) - // only blink once for each threshold - // FIXME: blinks at beginning of smooth_steps animation instead - // of the end, so it should blink when actual_level reaches a - // threshold, instead of when memorized_level does - // (one possible fix is to just remove mid-ramp blinks entirely, - // and just blink only when it hits the top while going up) - if ((memorized_level != actual_level) && ( - 0 // for easier syntax below - #ifdef BLINK_AT_RAMP_MIDDLE_1 - || (memorized_level == BLINK_AT_RAMP_MIDDLE_1) - #endif - #ifdef BLINK_AT_RAMP_MIDDLE_2 - || (memorized_level == BLINK_AT_RAMP_MIDDLE_2) - #endif - #ifdef BLINK_AT_RAMP_CEIL - // FIXME: only blink at top when going up, not down - || (memorized_level == mode_max) - #endif - #ifdef BLINK_AT_RAMP_FLOOR - || (memorized_level == mode_min) - #endif - )) { - blip(); - } - #endif - #if defined(BLINK_AT_STEPS) - uint8_t foo = cfg.ramp_style; - cfg.ramp_style = 1; - uint8_t nearest = nearest_level((int16_t)actual_level); - cfg.ramp_style = foo; - // only blink once for each threshold - if ((memorized_level != actual_level) && - (cfg.ramp_style == 0) && - (memorized_level == nearest) - ) - { - blip(); - } - #endif - set_level_and_therm_target(memorized_level); - #ifdef USE_SUNSET_TIMER - reset_sunset_timer(); - #endif - return EVENT_HANDLED; - } - // reverse ramp direction on hold release - else if ((event == EV_click1_hold_release) - || (event == EV_click2_hold_release)) { - ramp_direction = -ramp_direction; - #ifdef START_AT_MEMORIZED_LEVEL - save_config_wl(); - #endif - return EVENT_HANDLED; - } - - else if (event == EV_tick) { - // un-reverse after 1 second - if (arg == AUTO_REVERSE_TIME) ramp_direction = 1; - - #ifdef USE_SUNSET_TIMER - // reduce output if shutoff timer is active - if (sunset_timer) { - uint8_t dimmed_level = sunset_timer_orig_level * sunset_timer / sunset_timer_peak; - uint8_t dimmed_level_next = sunset_timer_orig_level * (sunset_timer-1) / sunset_timer_peak; - uint8_t dimmed_level_delta = dimmed_level - dimmed_level_next; - dimmed_level -= dimmed_level_delta * (sunset_ticks/TICKS_PER_SECOND) / 60; - if (dimmed_level < 1) dimmed_level = 1; - - #ifdef USE_SET_LEVEL_GRADUALLY - set_level_gradually(dimmed_level); - target_level = dimmed_level; - #else - set_level_and_therm_target(dimmed_level); - #endif - } - #endif // ifdef USE_SUNSET_TIMER - - #ifdef USE_SET_LEVEL_GRADUALLY - int16_t diff = gradual_target - actual_level; - static uint16_t ticks_since_adjust = 0; - ticks_since_adjust++; - if (diff) { - uint16_t ticks_per_adjust = 256; - if (diff < 0) { - //diff = -diff; - if (actual_level > THERM_FASTER_LEVEL) { - #ifdef THERM_HARD_TURBO_DROP - ticks_per_adjust >>= 2; - #endif - ticks_per_adjust >>= 2; - } - } else { - // rise at half speed - ticks_per_adjust <<= 1; - } - while (diff) { - ticks_per_adjust >>= 1; - //diff >>= 1; - diff /= 2; // because shifting produces weird behavior - } - if (ticks_since_adjust > ticks_per_adjust) - { - gradual_tick(); - ticks_since_adjust = 0; - } - } - #endif // ifdef USE_SET_LEVEL_GRADUALLY - return EVENT_HANDLED; - } - - #ifdef USE_THERMAL_REGULATION - // overheating: drop by an amount proportional to how far we are above the ceiling - else if (event == EV_temperature_high) { - #if 0 - blip(); - #endif - #ifdef THERM_HARD_TURBO_DROP - //if (actual_level > THERM_FASTER_LEVEL) { - if (actual_level == MAX_LEVEL) { - #ifdef USE_SET_LEVEL_GRADUALLY - set_level_gradually(THERM_FASTER_LEVEL); - target_level = THERM_FASTER_LEVEL; - #else - set_level_and_therm_target(THERM_FASTER_LEVEL); - #endif - } else - #endif - if (actual_level > MIN_THERM_STEPDOWN) { - int16_t stepdown = actual_level - arg; - if (stepdown < MIN_THERM_STEPDOWN) stepdown = MIN_THERM_STEPDOWN; - else if (stepdown > MAX_LEVEL) stepdown = MAX_LEVEL; - #ifdef USE_SET_LEVEL_GRADUALLY - set_level_gradually(stepdown); - #else - set_level(stepdown); - #endif - } - return EVENT_HANDLED; - } - // underheating: increase slowly if we're lower than the target - // (proportional to how low we are) - else if (event == EV_temperature_low) { - #if 0 - blip(); - #endif - if (actual_level < target_level) { - //int16_t stepup = actual_level + (arg>>1); - int16_t stepup = actual_level + arg; - if (stepup > target_level) stepup = target_level; - else if (stepup < MIN_THERM_STEPDOWN) stepup = MIN_THERM_STEPDOWN; - #ifdef USE_SET_LEVEL_GRADUALLY - set_level_gradually(stepup); - #else - set_level(stepup); - #endif - } - return EVENT_HANDLED; - } - #ifdef USE_SET_LEVEL_GRADUALLY - // temperature is within target window - // (so stop trying to adjust output) - else if (event == EV_temperature_okay) { - // if we're still adjusting output... stop after the current step - if (gradual_target > actual_level) - gradual_target = actual_level + 1; - else if (gradual_target < actual_level) - gradual_target = actual_level - 1; - return EVENT_HANDLED; - } - #endif // ifdef USE_SET_LEVEL_GRADUALLY - #endif // ifdef USE_THERMAL_REGULATION - - ////////// Every action below here is blocked in the simple UI ////////// - // That is, unless we specifically want to enable 3C for smooth/stepped selection in Simple UI - #if defined(USE_SIMPLE_UI) && !defined(USE_SIMPLE_UI_RAMPING_TOGGLE) - if (cfg.simple_ui_active) { - return EVENT_NOT_HANDLED; - } - #endif - - // 3 clicks: toggle smooth vs discrete ramping - // (and/or 6 clicks when there are multiple channel modes) - // (handle 3C here anyway, when all but 1 mode is disabled) - else if ((event == EV_3clicks) - #if NUM_CHANNEL_MODES > 1 - || (event == EV_6clicks) - ) { - // detect if > 1 channel mode is enabled, - // and if so, fall through so channel mode code can handle it - // otherwise, change the ramp style - if (event == EV_3clicks) { - uint8_t enabled = 0; - for (uint8_t m=0; m 1) - return EVENT_NOT_HANDLED; - } - #else - ) { - #endif - - cfg.ramp_style = !cfg.ramp_style; - save_config(); - #ifdef START_AT_MEMORIZED_LEVEL - save_config_wl(); - #endif - blip(); - memorized_level = nearest_level(actual_level); - set_level_and_therm_target(memorized_level); - #ifdef USE_SUNSET_TIMER - reset_sunset_timer(); - #endif - return EVENT_HANDLED; - } - - // If we allowed 3C in Simple UI, now block further actions - #if defined(USE_SIMPLE_UI) && defined(USE_SIMPLE_UI_RAMPING_TOGGLE) - if (cfg.simple_ui_active) { - return EVENT_NOT_HANDLED; - } - #endif - - // 3H: momentary turbo (on lights with no tint ramping) - // (or 4H when tint ramping is available) - else if ((event == EV_click3_hold) - #ifdef USE_CHANNEL_MODE_ARGS - || (event == EV_click4_hold) - #endif - ) { - #ifdef USE_CHANNEL_MODE_ARGS - // ramp tint if tint exists in this mode - if ((event == EV_click3_hold) - && (channel_has_args(channel_mode))) - return EVENT_NOT_HANDLED; - #endif - if (! arg) { // first frame only, to allow thermal regulation to work - #ifdef USE_2C_STYLE_CONFIG - uint8_t tl = style_2c ? MAX_LEVEL : turbo_level; - set_level_and_therm_target(tl); - #else - set_level_and_therm_target(turbo_level); - #endif - } - return EVENT_HANDLED; - } - else if ((event == EV_click3_hold_release) - #ifdef USE_CHANNEL_MODE_ARGS - || (event == EV_click4_hold_release) - #endif - ) { - #ifdef USE_CHANNEL_MODE_ARGS - // ramp tint if tint exists in this mode - if ((event == EV_click3_hold_release) - && (channel_has_args(channel_mode))) - return EVENT_NOT_HANDLED; - #endif - set_level_and_therm_target(memorized_level); - return EVENT_HANDLED; - } - - #ifdef USE_MOMENTARY_MODE - // 5 clicks: shortcut to momentary mode - else if (event == EV_5clicks) { - set_level(0); - set_state(momentary_state, 0); - return EVENT_HANDLED; - } - #endif - - #ifdef USE_RAMP_CONFIG - // 7H: configure this ramp mode - else if (event == EV_click7_hold) { - push_state(ramp_config_state, 0); - return EVENT_HANDLED; - } - #endif - - #ifdef USE_MANUAL_MEMORY - else if (event == EV_10clicks) { - // turn on manual memory and save current brightness - manual_memory_save(); - save_config(); - blink_once(); - return EVENT_HANDLED; - } - else if (event == EV_click10_hold) { - #ifdef USE_RAMP_EXTRAS_CONFIG - // let user configure a bunch of extra ramp options - push_state(ramp_extras_config_state, 0); - #else // manual mem, but no timer - // turn off manual memory; go back to automatic - if (0 == arg) { - cfg.manual_memory = 0; - save_config(); - blink_once(); - } - #endif - return EVENT_HANDLED; - } - #endif // ifdef USE_MANUAL_MEMORY - - return EVENT_NOT_HANDLED; -} - - -#ifdef USE_RAMP_CONFIG -void ramp_config_save(uint8_t step, uint8_t value) { - - // 0 = smooth ramp, 1 = stepped ramp, 2 = simple UI's ramp - uint8_t style = cfg.ramp_style; - #ifdef USE_SIMPLE_UI - if (current_state == simple_ui_config_state) style = 2; - #endif - - #if defined(USE_SIMPLE_UI) && defined(USE_2C_STYLE_CONFIG) - // simple UI config is weird... - // has some ramp extras after floor/ceil/steps - if (4 == step) { - cfg.ramp_2c_style_simple = value; - } - else - #endif - - // save adjusted value to the correct slot - if (value) { - // ceiling value is inverted - if (step == 2) value = MAX_LEVEL + 1 - value; - - // which option are we configuring? - // TODO? maybe rearrange definitions to avoid the need for this table - // (move all ramp values into a single array?) - uint8_t *steps[] = { cfg.ramp_floors, cfg.ramp_ceils, cfg.ramp_stepss }; - uint8_t *option; - option = steps[step-1]; - option[style] = value; - } -} - -uint8_t ramp_config_state(Event event, uint16_t arg) { - #ifdef USE_RAMP_SPEED_CONFIG - const uint8_t num_config_steps = 3; - #else - uint8_t num_config_steps = cfg.ramp_style + 2; - #endif - return config_state_base(event, arg, - num_config_steps, ramp_config_save); -} - -#ifdef USE_SIMPLE_UI -uint8_t simple_ui_config_state(Event event, uint16_t arg) { - #if defined(USE_2C_STYLE_CONFIG) - #define SIMPLE_UI_NUM_MENU_ITEMS 4 - #else - #define SIMPLE_UI_NUM_MENU_ITEMS 3 - #endif - return config_state_base(event, arg, - SIMPLE_UI_NUM_MENU_ITEMS, - ramp_config_save); -} -#endif -#endif // #ifdef USE_RAMP_CONFIG - -#ifdef USE_RAMP_EXTRAS_CONFIG -void ramp_extras_config_save(uint8_t step, uint8_t value) { - // item 1: disable manual memory, go back to automatic - if (manual_memory_config_step == step) { - cfg.manual_memory = 0; - } - - #ifdef USE_MANUAL_MEMORY_TIMER - // item 2: set manual memory timer duration - // FIXME: should be limited to (65535 / SLEEP_TICKS_PER_MINUTE) - // to avoid overflows or impossibly long timeouts - // (by default, the effective limit is 145, but it allows up to 255) - else if (manual_memory_timer_config_step == step) { - cfg.manual_memory_timer = value; - } - #endif - - #ifdef USE_RAMP_AFTER_MOON_CONFIG - // item 3: ramp up after hold-from-off for moon? - // 0 = yes, ramp after moon - // 1+ = no, stay at moon - else if (dont_ramp_after_moon_config_step == step) { - cfg.dont_ramp_after_moon = value; - } - #endif - - #ifdef USE_2C_STYLE_CONFIG - // item 4: Anduril 1 2C turbo, or Anduril 2 2C ceiling? - // 1 = Anduril 1, 2C turbo - // 2+ = Anduril 2, 2C ceiling - else if (ramp_2c_style_config_step == step) { - cfg.ramp_2c_style = value; - } - #endif - - #ifdef USE_SMOOTH_STEPS - else if (smooth_steps_style_config_step == step) { - cfg.smooth_steps_style = value; - } - #endif -} - -uint8_t ramp_extras_config_state(Event event, uint16_t arg) { - return config_state_base(event, arg, - ramp_extras_config_num_steps - 1, - ramp_extras_config_save); -} -#endif - -#ifdef USE_GLOBALS_CONFIG -void globals_config_save(uint8_t step, uint8_t value) { - if (0) {} - #if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING) - else if (step == tint_style_config_step) { cfg.tint_ramp_style = value; } - #endif - #ifdef USE_JUMP_START - else if (step == jump_start_config_step) { cfg.jump_start_level = value; } - #endif -} - -uint8_t globals_config_state(Event event, uint16_t arg) { - return config_state_base(event, arg, - globals_config_num_steps - 1, - globals_config_save); -} -#endif - -// find the ramp level closest to the target, -// using only the levels which are allowed in the current state -uint8_t nearest_level(int16_t target) { - // using int16_t here saves us a bunch of logic elsewhere, - // by allowing us to correct for numbers < 0 or > 255 in one central place - - // ensure all globals are correct - ramp_update_config(); - - // bounds check - uint8_t mode_min = ramp_floor; - uint8_t mode_max = ramp_ceil; - uint8_t num_steps = cfg.ramp_stepss[1 - #ifdef USE_SIMPLE_UI - + cfg.simple_ui_active - #endif - ]; - // special case for 1-step ramp... use halfway point between floor and ceiling - if (cfg.ramp_style && (1 == num_steps)) { - uint8_t mid = (mode_max + mode_min) >> 1; - return mid; - } - if (target < mode_min) return mode_min; - if (target > mode_max) return mode_max; - // the rest isn't relevant for smooth ramping - if (! cfg.ramp_style) return target; - - uint8_t ramp_range = mode_max - mode_min; - ramp_discrete_step_size = ramp_range / (num_steps-1); - uint8_t this_level = mode_min; - - for(uint8_t i=0; i>1)) - return this_level; - } - return this_level; -} - -// ensure ramp globals are correct -void ramp_update_config() { - uint8_t which = cfg.ramp_style; - #ifdef USE_SIMPLE_UI - if (cfg.simple_ui_active) { which = 2; } - #endif - - ramp_floor = cfg.ramp_floors[which]; - ramp_ceil = cfg.ramp_ceils[which]; -} - -#if defined(USE_THERMAL_REGULATION) || defined(USE_SMOOTH_STEPS) -void set_level_and_therm_target(uint8_t level) { - #ifdef USE_THERMAL_REGULATION - target_level = level; - #endif - #ifdef USE_SMOOTH_STEPS - // if adjusting by more than 1 ramp level, - // animate the step change (if smooth steps enabled) - uint8_t diff = (level > actual_level) - ? (level - actual_level) : (actual_level - level); - if (smooth_steps_in_progress - || (cfg.smooth_steps_style && (diff > 1))) - set_level_smooth(level, 4); - else - #endif - set_level(level); -} -#else -#define set_level_and_therm_target(level) set_level(level) -#endif - -void manual_memory_restore() { - memorized_level = cfg.manual_memory; - #if NUM_CHANNEL_MODES > 1 - channel_mode = cfg.channel_mode = cfg.manual_memory_channel_mode; - #endif - #ifdef USE_CHANNEL_MODE_ARGS - for (uint8_t i=0; i 1 - cfg.manual_memory_channel_mode = channel_mode; - #endif - #ifdef USE_CHANNEL_MODE_ARGS - for (uint8_t i=0; i= 3 - #ifndef BLINK_AT_RAMP_MIDDLE_1 - #define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135 - #ifndef BLINK_AT_RAMP_MIDDLE_2 - #define BLINK_AT_RAMP_MIDDLE_2 MAX_1x7135 - #endif - #endif - #else - #ifndef BLINK_AT_RAMP_MIDDLE_1 - #define BLINK_AT_RAMP_MIDDLE_1 MAX_1x7135 - #endif - #endif -#endif - - -// ramping mode and its related config mode -uint8_t steady_state(Event event, uint16_t arg); - -#ifdef USE_RAMP_CONFIG -uint8_t ramp_config_state(Event event, uint16_t arg); -void ramp_config_save(uint8_t step, uint8_t value); -#ifdef USE_SIMPLE_UI -uint8_t simple_ui_config_state(Event event, uint16_t arg); -#endif -#endif - -#if defined(USE_MANUAL_MEMORY_TIMER) || defined(USE_RAMP_AFTER_MOON_CONFIG) || defined(USE_2C_STYLE_CONFIG) || defined(USE_AUTO_SUNSET) -#define USE_RAMP_EXTRAS_CONFIG -#endif -#ifdef USE_RAMP_EXTRAS_CONFIG -uint8_t ramp_extras_config_state(Event event, uint16_t arg); -#endif - -// calculate the nearest ramp level which would be valid at the moment -// (is a no-op for smooth ramp, but limits discrete ramp to only the -// correct levels for the user's config) -uint8_t nearest_level(int16_t target); - -// ensure ramp globals are correct -void ramp_update_config(); - -#if defined(USE_THERMAL_REGULATION) || defined(USE_SMOOTH_STEPS) -// brightness before thermal step-down -uint8_t target_level = 0; -void set_level_and_therm_target(uint8_t level); -#else -#define set_level_and_therm_target(level) set_level(level) -#endif - - -// brightness control -uint8_t memorized_level = DEFAULT_LEVEL; -#ifdef USE_MANUAL_MEMORY - void manual_memory_restore(); - void manual_memory_save(); - #ifndef DEFAULT_MANUAL_MEMORY - #define DEFAULT_MANUAL_MEMORY 0 - #endif - #ifdef USE_MANUAL_MEMORY_TIMER - #ifndef DEFAULT_MANUAL_MEMORY_TIMER - #define DEFAULT_MANUAL_MEMORY_TIMER 0 - #endif - #endif -#endif - -#ifndef DEFAULT_2C_STYLE_SIMPLE - #define DEFAULT_2C_STYLE_SIMPLE 0 -#endif - -#ifdef USE_2C_STYLE_CONFIG -#ifndef DEFAULT_2C_STYLE -#define DEFAULT_2C_STYLE 2 -#endif - -#ifdef USE_2C_MAX_TURBO -#error Cannot use USE_2C_MAX_TURBO and USE_2C_STYLE_CONFIG at the same time. -#endif -#endif - -#ifdef USE_RAMP_SPEED_CONFIG -#define ramp_speed (cfg.ramp_stepss[0]) -#endif -#ifdef USE_RAMP_AFTER_MOON_CONFIG -#ifndef DEFAULT_DONT_RAMP_AFTER_MOON -#define DEFAULT_DONT_RAMP_AFTER_MOON 0 -#endif -#endif -// current values, regardless of style -uint8_t ramp_floor = RAMP_SMOOTH_FLOOR; -uint8_t ramp_ceil = RAMP_SMOOTH_CEIL; - -uint8_t ramp_discrete_step_size; // don't set this - -#ifdef USE_SUNSET_TIMER -uint8_t sunset_timer_orig_level = 0; -void reset_sunset_timer(); -#endif - -#ifdef USE_RAMP_EXTRAS_CONFIG -typedef enum { - ramp_extras_cfg_zero = 0, - manual_memory_config_step, - #ifdef USE_MANUAL_MEMORY_TIMER - manual_memory_timer_config_step, - #endif - #ifdef USE_RAMP_AFTER_MOON_CONFIG - dont_ramp_after_moon_config_step, - #endif - #ifdef USE_2C_STYLE_CONFIG - ramp_2c_style_config_step, - #endif - #ifdef USE_SMOOTH_STEPS - smooth_steps_style_config_step, - #endif - ramp_extras_config_num_steps -} ramp_extras_config_steps_e; -#endif - -#ifdef USE_GLOBALS_CONFIG -typedef enum { - globals_cfg_zero = 0, - #if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING) - tint_style_config_step, - #endif - #ifdef USE_JUMP_START - jump_start_config_step, - #endif - globals_config_num_steps -} globals_config_steps_e; - -void globals_config_save(uint8_t step, uint8_t value); -uint8_t globals_config_state(Event event, uint16_t arg); -#endif - diff --git a/spaghetti-monster/anduril/smooth-steps.c b/spaghetti-monster/anduril/smooth-steps.c deleted file mode 100644 index 9631e41..0000000 --- a/spaghetti-monster/anduril/smooth-steps.c +++ /dev/null @@ -1,47 +0,0 @@ -// smooth-steps.c: Smooth step adjustments for Anduril. -// Copyright (C) 2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "smooth-steps.h" - -#ifdef USE_SMOOTH_STEPS - -// one iteration of main loop() -void smooth_steps_iter() { - if (actual_level == smooth_steps_target) { - set_level(smooth_steps_target); - smooth_steps_in_progress = 0; - // restore prev_level when animation ends - prev_level = smooth_steps_start; - } - else if (smooth_steps_target > actual_level) { - // power-linear(ish) ascent - // (jump by ~20% of remaining distance on each frame) - uint8_t diff = smooth_steps_target - actual_level; - uint8_t this = diff / smooth_steps_speed; - if (!this) this = 1; - set_level(actual_level + this); - nice_delay_ms(10); - } else { - // ramp-linear descent - // (jump by 1 on each frame, frame rate gives constant total time) - uint8_t diff = smooth_steps_start - smooth_steps_target; - uint16_t delay = 1 + (30 * smooth_steps_speed / diff); - set_level(actual_level - 1); - // TODO? if delay < one PWM cycle, this can look a little weird - nice_delay_ms(delay); - } -} - -void set_level_smooth(uint8_t level, uint8_t speed) { - smooth_steps_target = level; - smooth_steps_speed = speed; // higher = slower - smooth_steps_in_progress = 1; - // for setting prev_level after animation ends - smooth_steps_start = actual_level; -} - -#endif - diff --git a/spaghetti-monster/anduril/smooth-steps.h b/spaghetti-monster/anduril/smooth-steps.h deleted file mode 100644 index a553af2..0000000 --- a/spaghetti-monster/anduril/smooth-steps.h +++ /dev/null @@ -1,19 +0,0 @@ -// smooth-steps.h: Smooth step adjustments for Anduril. -// Copyright (C) 2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#ifdef USE_SMOOTH_STEPS - -uint8_t smooth_steps_start; -uint8_t smooth_steps_target; -uint8_t smooth_steps_in_progress; -uint8_t smooth_steps_speed; - -void smooth_steps_iter(); - -void set_level_smooth(uint8_t level, uint8_t speed); - -#endif - diff --git a/spaghetti-monster/anduril/sos-mode.c b/spaghetti-monster/anduril/sos-mode.c deleted file mode 100644 index 2a4e97c..0000000 --- a/spaghetti-monster/anduril/sos-mode.c +++ /dev/null @@ -1,56 +0,0 @@ -// sos-mode.c: SOS mode for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "sos-mode.h" - -#ifdef USE_SOS_MODE_IN_BLINKY_GROUP -uint8_t sos_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: next blinky mode - else if (event == EV_2clicks) { - #if defined(USE_BATTCHECK_MODE) - set_state(battcheck_state, 0); - #elif defined(USE_THERMAL_REGULATION) - set_state(tempcheck_state, 0); - #elif defined(USE_BEACON_MODE) - set_state(beacon_state, 0); - #endif - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} -#endif - -void sos_blink(uint8_t num, uint8_t dah) { - #define DIT_LENGTH 200 - for (; num > 0; num--) { - set_level(memorized_level); - nice_delay_ms(DIT_LENGTH); - if (dah) { // dah is 3X as long as a dit - nice_delay_ms(DIT_LENGTH*2); - } - set_level(0); - // one "off" dit between blinks - nice_delay_ms(DIT_LENGTH); - } - // three "off" dits (or one "dah") between letters - // (except for SOS, which is collectively treated as a single "letter") - //nice_delay_ms(DIT_LENGTH*2); -} - -inline void sos_mode_iter() { - // one iteration of main loop() - //nice_delay_ms(1000); - sos_blink(3, 0); // S - sos_blink(3, 1); // O - sos_blink(3, 0); // S - nice_delay_ms(2000); -} - diff --git a/spaghetti-monster/anduril/sos-mode.h b/spaghetti-monster/anduril/sos-mode.h deleted file mode 100644 index 5af61be..0000000 --- a/spaghetti-monster/anduril/sos-mode.h +++ /dev/null @@ -1,11 +0,0 @@ -// sos-mode.h: SOS mode for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#ifdef USE_SOS_MODE_IN_BLINKY_GROUP -// automatic SOS emergency signal -uint8_t sos_state(Event event, uint16_t arg); -#endif - diff --git a/spaghetti-monster/anduril/steps.py b/spaghetti-monster/anduril/steps.py deleted file mode 100755 index e19c9a6..0000000 --- a/spaghetti-monster/anduril/steps.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python - -"""steps.py: Calculate the stepped ramp levels used by Anduril. -Usage: steps.py floor ceiling num_steps -For example: - > ./steps.py 1 150 3 - 1: 1 - 2: 75 - 3: 150 -""" - -def main(args): - floor, ceil, steps = [int(x) for x in args[:3]] - for i in range(steps): - guess = floor + (i * (float(ceil-floor)/(steps-1))) - this = nearest_level(guess, floor, ceil, steps) - #print('%i: %i (guess: %i)' % (i+1, this, guess)) - print('%i: %i' % (i+1, this)) - - -def nearest_level(target, floor, ceil, steps): - """Copied/adapted from anduril.c""" - # bounds check - # using int16_t here saves us a bunch of logic elsewhere, - # by allowing us to correct for numbers < 0 or > 255 in one central place - mode_min = floor; - mode_max = ceil; - - if (target < mode_min): return mode_min; - if (target > mode_max): return mode_max; - # the rest isn't relevant for smooth ramping - #if (! ramp_style): return target; - - ramp_range = ceil - floor; - ramp_discrete_step_size = int(ramp_range / (steps-1)); - this_level = floor; - - for i in range(steps): - this_level = floor + (i * int(ramp_range) / (steps-1)); - diff = int(target - this_level); - if (diff < 0): diff = -diff; - if (diff <= (ramp_discrete_step_size>>1)): - return this_level; - - return this_level; - - -if __name__ == "__main__": - import sys - main(sys.argv[1:]) - diff --git a/spaghetti-monster/anduril/strobe-modes-fsm.h b/spaghetti-monster/anduril/strobe-modes-fsm.h deleted file mode 100644 index 4d948ed..0000000 --- a/spaghetti-monster/anduril/strobe-modes-fsm.h +++ /dev/null @@ -1,55 +0,0 @@ -// strobe-modes-fsm.h: FSM config for strobe modes in Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -// enable the random number generator if we need it -#if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE) -#define USE_PSEUDO_RAND -#endif - -// party strobe uses really short pulses -#ifdef USE_PARTY_STROBE_MODE -#define USE_DELAY_ZERO -#endif - -// candle mode is basically a bunch of stacked random triangle waves -#if defined(USE_CANDLE_MODE) -#define USE_TRIANGLE_WAVE -#endif - -// the presence of strobe mode(s) affects how many eeprom bytes we need, -// so it's relevant for FSM configuration -#if defined(USE_CANDLE_MODE) || defined(USE_BIKE_FLASHER_MODE) || defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) || defined(USE_LIGHTNING_MODE) -#define USE_STROBE_STATE -#endif - -// internal numbering for strobe modes -#ifdef USE_STROBE_STATE -typedef enum { - #ifdef USE_PARTY_STROBE_MODE - party_strobe_e, - #endif - #ifdef USE_TACTICAL_STROBE_MODE - tactical_strobe_e, - #endif - #ifdef USE_POLICE_COLOR_STROBE_MODE - police_color_strobe_e, - #endif - #ifdef USE_LIGHTNING_MODE - lightning_storm_e, - #endif - #ifdef USE_CANDLE_MODE - candle_mode_e, - #endif - #ifdef USE_BIKE_FLASHER_MODE - bike_flasher_e, - #endif - strobe_mode_END -} strobe_mode_te; - -//const int NUM_STROBES = strobe_mode_END; -#define NUM_STROBES strobe_mode_END -#endif - diff --git a/spaghetti-monster/anduril/strobe-modes.c b/spaghetti-monster/anduril/strobe-modes.c deleted file mode 100644 index ad17964..0000000 --- a/spaghetti-monster/anduril/strobe-modes.c +++ /dev/null @@ -1,332 +0,0 @@ -// strobe-modes.c: Strobe modes for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "strobe-modes.h" - -#ifdef USE_STROBE_STATE -uint8_t strobe_state(Event event, uint16_t arg) { - static int8_t ramp_direction = 1; - - // 'st' reduces ROM size slightly - strobe_mode_te st = current_strobe_type; - - #ifdef USE_MOMENTARY_MODE - momentary_mode = 1; // 0 = ramping, 1 = strobes - #endif - - #ifdef USE_CANDLE_MODE - // pass all events to candle mode, when it's active - // (the code is in its own pseudo-state to keep things cleaner) - if (st == candle_mode_e) { - candle_mode_state(event, arg); - } - #endif - - if (0) {} // placeholder - // init anything which needs to be initialized - else if (event == EV_enter_state) { - current_strobe_type = cfg.strobe_type; - ramp_direction = 1; - return EVENT_HANDLED; - } - // 1 click: off - else if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: rotate through strobe/flasher modes - else if (event == EV_2clicks) { - current_strobe_type = cfg.strobe_type = (st + 1) % NUM_STROBES; - save_config(); - return EVENT_HANDLED; - } - #if (NUM_CHANNEL_MODES > 1) && defined(USE_CHANNEL_PER_STROBE) - // 3 clicks: rotate through channel modes for the current strobe - else if (event == EV_3clicks) { - // TODO: maybe skip aux modes? - set_channel_mode((channel_mode + 1) % NUM_CHANNEL_MODES); - cfg.strobe_channels[st] = channel_mode; - save_config(); - return EVENT_HANDLED; - } - #endif - // 4 clicks: rotate backward through strobe/flasher modes - else if (event == EV_4clicks) { - current_strobe_type = cfg.strobe_type = (st - 1 + NUM_STROBES) % NUM_STROBES; - save_config(); - return EVENT_HANDLED; - } - // hold: change speed (go faster) - // or change brightness (brighter) - else if (event == EV_click1_hold) { - if (0) {} // placeholder - - // party / tactical strobe faster - #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) - #ifdef USE_TACTICAL_STROBE_MODE - else if (st <= tactical_strobe_e) { - #else - else if (st == party_strobe_e) { - #endif - if ((arg & 1) == 0) { - uint8_t d = cfg.strobe_delays[st]; - d -= ramp_direction; - if (d < 8) d = 8; - else if (d > 254) d = 254; - cfg.strobe_delays[st] = d; - } - } - #endif - - // lightning has no adjustments - //else if (st == lightning_storm_e) {} - - // biking mode brighter - #ifdef USE_BIKE_FLASHER_MODE - else if (st == bike_flasher_e) { - cfg.bike_flasher_brightness += ramp_direction; - if (cfg.bike_flasher_brightness < 2) cfg.bike_flasher_brightness = 2; - else if (cfg.bike_flasher_brightness > MAX_BIKING_LEVEL) cfg.bike_flasher_brightness = MAX_BIKING_LEVEL; - set_level(cfg.bike_flasher_brightness); - } - #endif - - return EVENT_HANDLED; - } - // reverse ramp direction on hold release - // ... and save new strobe settings - else if (event == EV_click1_hold_release) { - ramp_direction = -ramp_direction; - save_config(); - return EVENT_HANDLED; - } - // click, hold: change speed (go slower) - // or change brightness (dimmer) - else if (event == EV_click2_hold) { - ramp_direction = 1; - - if (0) {} // placeholder - - // party / tactical strobe slower - #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) - #ifdef USE_TACTICAL_STROBE_MODE - else if (st <= tactical_strobe_e) { - #else - else if (st == party_strobe_e) { - #endif - if ((arg & 1) == 0) { - if (cfg.strobe_delays[st] < 255) cfg.strobe_delays[st] ++; - } - } - #endif - - // lightning has no adjustments - //else if (st == lightning_storm_e) {} - - // biking mode dimmer - #ifdef USE_BIKE_FLASHER_MODE - else if (st == bike_flasher_e) { - if (cfg.bike_flasher_brightness > 2) - cfg.bike_flasher_brightness --; - set_level(cfg.bike_flasher_brightness); - } - #endif - - return EVENT_HANDLED; - } - // release hold: save new strobe settings - else if (event == EV_click2_hold_release) { - save_config(); - return EVENT_HANDLED; - } - #ifdef USE_MOMENTARY_MODE - // 5 clicks: go to momentary mode (momentary strobe) - else if (event == EV_5clicks) { - set_state(momentary_state, 0); - set_level(0); - return EVENT_HANDLED; - } - #endif - #if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE) - // clock tick: bump the random seed - else if (event == EV_tick) { - // un-reverse after 1 second - if (arg == AUTO_REVERSE_TIME) ramp_direction = 1; - - pseudo_rand_seed += arg; - return EVENT_HANDLED; - } - #endif - return EVENT_NOT_HANDLED; -} - -// runs repeatedly in FSM loop() whenever UI is in strobe_state or momentary strobe -inline void strobe_state_iter() { - uint8_t st = current_strobe_type; // can't use switch() on an enum - - #if (NUM_CHANNEL_MODES > 1) && defined(USE_CHANNEL_PER_STROBE) - // remember channel mode for each strobe - channel_mode = cfg.strobe_channels[st]; - #endif - - switch(st) { - #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) - #ifdef USE_PARTY_STROBE_MODE - case party_strobe_e: - #endif - #ifdef USE_TACTICAL_STROBE_MODE - case tactical_strobe_e: - #endif - party_tactical_strobe_mode_iter(st); - break; - #endif - - #ifdef USE_POLICE_COLOR_STROBE_MODE - case police_color_strobe_e: - police_color_strobe_iter(); - break; - #endif - - #ifdef USE_LIGHTNING_MODE - case lightning_storm_e: - lightning_storm_iter(); - break; - #endif - - #ifdef USE_BIKE_FLASHER_MODE - case bike_flasher_e: - bike_flasher_iter(); - break; - #endif - } -} -#endif // ifdef USE_STROBE_STATE - -#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) -inline void party_tactical_strobe_mode_iter(uint8_t st) { - // one iteration of main loop() - uint8_t del = cfg.strobe_delays[st]; - // TODO: make tac strobe brightness configurable? - set_level(STROBE_BRIGHTNESS); - if (0) {} // placeholder - #ifdef USE_PARTY_STROBE_MODE - else if (st == party_strobe_e) { // party strobe - #ifdef PARTY_STROBE_ONTIME - nice_delay_ms(PARTY_STROBE_ONTIME); - #else - if (del < 42) delay_zero(); - else nice_delay_ms(1); - #endif - } - #endif - #ifdef USE_TACTICAL_STROBE_MODE - else { //tactical strobe - nice_delay_ms(del >> 1); - } - #endif - set_level(STROBE_OFF_LEVEL); - nice_delay_ms(del); // no return check necessary on final delay -} -#endif - -#ifdef USE_POLICE_COLOR_STROBE_MODE -inline void police_color_strobe_iter() { - // one iteration of main loop() - uint8_t del = 66; - // TODO: make police strobe brightness configurable - uint8_t bright = memorized_level; - //uint8_t channel = channel_mode; - - for (uint8_t i=0; i<10; i++) { - if (0 == i) set_channel_mode(POLICE_COLOR_STROBE_CH1); - else if (5 == i) set_channel_mode(POLICE_COLOR_STROBE_CH2); - set_level(bright); - nice_delay_ms(del >> 1); - set_level(STROBE_OFF_LEVEL); - nice_delay_ms(del); - } - - // restore the channel when done - //set_channel_mode(channel); - channel_mode = cfg.channel_mode; -} -#endif - -#ifdef USE_LIGHTNING_MODE -inline void lightning_storm_iter() { - // one iteration of main loop() - int16_t brightness; - uint16_t rand_time; - - // turn the emitter on at a random level, - // for a random amount of time between 1ms and 32ms - //rand_time = 1 << (pseudo_rand() % 7); - rand_time = pseudo_rand() & 63; - brightness = 1 << (pseudo_rand() % 7); // 1, 2, 4, 8, 16, 32, 64 - brightness += 1 << (pseudo_rand() % 5); // 2 to 80 now - brightness += pseudo_rand() % brightness; // 2 to 159 now (w/ low bias) - if (brightness > MAX_LEVEL) brightness = MAX_LEVEL; - set_level(brightness); - nice_delay_ms(rand_time); - - // decrease the brightness somewhat more gradually, like lightning - uint8_t stepdown = brightness >> 3; - if (stepdown < 1) stepdown = 1; - while(brightness > 1) { - nice_delay_ms(rand_time); - brightness -= stepdown; - if (brightness < 0) brightness = 0; - set_level(brightness); - /* - if ((brightness < MAX_LEVEL/2) && (! (pseudo_rand() & 15))) { - brightness <<= 1; - set_level(brightness); - } - */ - if (! (pseudo_rand() & 3)) { - nice_delay_ms(rand_time); - set_level(brightness>>1); - } - } - - // turn the emitter off, - // for a random amount of time between 1ms and 8192ms - // (with a low bias) - rand_time = 1 << (pseudo_rand() % 13); - rand_time += pseudo_rand() % rand_time; - set_level(0); - nice_delay_ms(rand_time); // no return check necessary on final delay -} -#endif - -#ifdef USE_BIKE_FLASHER_MODE -#ifndef BIKE_STROBE_ONTIME -#define BIKE_STROBE_ONTIME 0 -#endif -inline void bike_flasher_iter() { - // one iteration of main loop() - uint8_t burst = cfg.bike_flasher_brightness << 1; - if (burst > MAX_LEVEL) burst = MAX_LEVEL; - for(uint8_t i=0; i<4; i++) { - set_level(burst); - nice_delay_ms(5 + BIKE_STROBE_ONTIME); - set_level(cfg.bike_flasher_brightness); - nice_delay_ms(65); - } - nice_delay_ms(720); // no return check necessary on final delay - set_level(0); -} -#endif - -#ifdef USE_CANDLE_MODE -#include "candle-mode.c" -#endif - - -#ifdef USE_BORING_STROBE_STATE -#include "ff-strobe-modes.c" -#endif - diff --git a/spaghetti-monster/anduril/strobe-modes.h b/spaghetti-monster/anduril/strobe-modes.h deleted file mode 100644 index 7dc1df4..0000000 --- a/spaghetti-monster/anduril/strobe-modes.h +++ /dev/null @@ -1,71 +0,0 @@ -// strobe-modes.h: Strobe modes for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#ifdef USE_STROBE_STATE - -strobe_mode_te current_strobe_type; - -// which strobe mode is active? -#ifdef USE_CANDLE_MODE - #define DEFAULT_STROBE candle_mode_e -#else - #define DEFAULT_STROBE 0 -#endif - -#endif // ifdef USE_STROBE_STATE - - -// full FET strobe can be a bit much... use max regulated level instead, -// if there's a bright enough regulated level -#ifndef STROBE_BRIGHTNESS -#ifdef MAX_Nx7135 -#define STROBE_BRIGHTNESS MAX_Nx7135 -#else -#define STROBE_BRIGHTNESS MAX_LEVEL -#endif -#endif - -// some drivers need to keep the regulator chip on between pulses, -// so set this to 1 to keep the light on at moon mode between pulses, -// and thus keep the regulator powered up for the next flash -#ifndef STROBE_OFF_LEVEL -#define STROBE_OFF_LEVEL 0 -#endif - -// party and tactical strobes -#ifdef USE_STROBE_STATE -uint8_t strobe_state(Event event, uint16_t arg); -inline void strobe_state_iter(); -#endif - -#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) -inline void party_tactical_strobe_mode_iter(uint8_t st); -#endif - -#ifdef USE_POLICE_COLOR_STROBE_MODE -inline void police_color_strobe_iter(); -#endif - -#ifdef USE_LIGHTNING_MODE -inline void lightning_storm_iter(); -#endif - -// bike mode config options -#ifdef USE_BIKE_FLASHER_MODE -#define MAX_BIKING_LEVEL 120 // should be 127 or less -inline void bike_flasher_iter(); -#endif - -#ifdef USE_CANDLE_MODE -#include "candle-mode.h" -#endif - - -#if defined(USE_POLICE_STROBE_MODE) || defined(USE_SOS_MODE_IN_FF_GROUP) -#define USE_BORING_STROBE_STATE -#include "ff-strobe-modes.h" -#endif - diff --git a/spaghetti-monster/anduril/sunset-timer.c b/spaghetti-monster/anduril/sunset-timer.c deleted file mode 100644 index e4fc512..0000000 --- a/spaghetti-monster/anduril/sunset-timer.c +++ /dev/null @@ -1,60 +0,0 @@ -// sunset-timer.c: Sunset / candle auto-shutoff functions for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "sunset-timer.h" - -uint8_t sunset_timer_state(Event event, uint16_t arg) { - - #if defined(USE_SIMPLE_UI) && !defined(USE_EXTENDED_SIMPLE_UI) - // No timer functions in Simple UI - if (cfg.simple_ui_active) return EVENT_NOT_HANDLED; - #endif - - // reset on start - if (event == EV_enter_state) { - sunset_timer = 0; - sunset_ticks = 0; - return EVENT_HANDLED; - } - // hold: maybe "bump" the timer if it's active and almost expired - else if (event == EV_hold) { - // ramping up should "bump" the timer to extend the deadline a bit - if ((sunset_timer > 0) && (sunset_timer < 4)) { - sunset_timer = 3; // 3 minutes - sunset_timer_peak = 3; - sunset_ticks = 0; // re-start current "minute" - } - } - // 5H: add 5m to timer, per second, until released - else if (event == EV_click5_hold) { - if (0 == (arg % TICKS_PER_SECOND)) { - if (sunset_timer < (255 - SUNSET_TIMER_UNIT)) { - // add a few minutes to the timer - sunset_timer += SUNSET_TIMER_UNIT; - sunset_timer_peak = sunset_timer; // reset ceiling - sunset_ticks = 0; // reset phase - // let the user know something happened - blink_once(); - } - } - return EVENT_HANDLED; - } - // tick: count down until time expires - else if (event == EV_tick) { - // time passed - sunset_ticks ++; - // did we reach a minute mark? - if (sunset_ticks >= TICKS_PER_MINUTE) { - sunset_ticks = 0; - if (sunset_timer > 0) { - sunset_timer --; - } - } - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - diff --git a/spaghetti-monster/anduril/sunset-timer.h b/spaghetti-monster/anduril/sunset-timer.h deleted file mode 100644 index 963804e..0000000 --- a/spaghetti-monster/anduril/sunset-timer.h +++ /dev/null @@ -1,17 +0,0 @@ -// sunset-timer.h: Sunset / candle auto-shutoff functions for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -// how many minutes to add each time the user "bumps" the timer? -#define SUNSET_TIMER_UNIT 5 - -#define TICKS_PER_MINUTE (TICKS_PER_SECOND*60) - -// automatic shutoff timer -uint8_t sunset_timer = 0; // minutes remaining in countdown -uint8_t sunset_timer_peak = 0; // total minutes in countdown -uint16_t sunset_ticks = 0; // counts from 0 to TICKS_PER_MINUTE, then repeats -uint8_t sunset_timer_state(Event event, uint16_t arg); - diff --git a/spaghetti-monster/anduril/tactical-mode.c b/spaghetti-monster/anduril/tactical-mode.c deleted file mode 100644 index 0035496..0000000 --- a/spaghetti-monster/anduril/tactical-mode.c +++ /dev/null @@ -1,109 +0,0 @@ -// tactical-mode.c: Tactical (ish) mode for Anduril. -// Copyright (C) 2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "tactical-mode.h" - - -uint8_t tactical_state(Event event, uint16_t arg) { - // momentary(ish) tactical mode - uint8_t mem_lvl = memorized_level; // save this to restore it later - uint8_t ret = EVENT_NOT_HANDLED; - - // button is being held - if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { - // 1H: 1st level - // 2H: 2nd level - // 3H: 3rd level - // 4+: nothing - momentary_active = 0; - ret = EVENT_HANDLED; - uint8_t click = event & 0x0f; // click number - if (click <= 3) { - momentary_active = 1; - uint8_t lvl; - lvl = cfg.tactical_levels[click-1]; - if ((1 <= lvl) && (lvl <= RAMP_SIZE)) { // steady output - memorized_level = lvl; - momentary_mode = 0; - #if NUM_CHANNEL_MODES > 1 - // use ramp mode's channel - channel_mode = cfg.channel_mode; - #endif - } else { // momentary strobe mode - momentary_mode = 1; - if (lvl > RAMP_SIZE) { - current_strobe_type = (lvl - RAMP_SIZE - 1) % strobe_mode_END; - } - } - } - } - // button was released - else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { - momentary_active = 0; - set_level(0); - interrupt_nice_delays(); // stop animations in progress - } - - // delegate to momentary mode while button is pressed - if (momentary_active) { - momentary_state(event, arg); - } - - memorized_level = mem_lvl; // restore temporarily overridden mem level - - // copy lockout mode's aux LED and sleep behaviors - if (event == EV_enter_state) { - lockout_state(event, arg); - } - else if (event == EV_tick) { - if (! momentary_active) { - return lockout_state(event, arg); - } - return EVENT_HANDLED; - } - else if (event == EV_sleep_tick) { - return lockout_state(event, arg); - } - - // 6 clicks: exit and turn off - else if (event == EV_6clicks) { - blink_once(); - set_state(off_state, 0); - return EVENT_HANDLED; - } - - ////////// Every action below here is blocked in the simple UI ////////// - // (unnecessary since this entire mode is blocked in simple UI) - /* - #ifdef USE_SIMPLE_UI - if (cfg.simple_ui_active) { - return EVENT_NOT_HANDLED; - } - #endif - */ - - // 7H: configure tactical mode - else if (event == EV_click7_hold) { - push_state(tactical_config_state, 0); - return EVENT_HANDLED; - } - - return ret; -} - -void tactical_config_save(uint8_t step, uint8_t value) { - // update tac mode values - // 3 values - // each value is 1 to 150, or other: - // - 1..150 is a ramp level - // - other means "strobe mode" - cfg.tactical_levels[step - 1] = value; -} - -uint8_t tactical_config_state(Event event, uint16_t arg) { - return config_state_base(event, arg, 3, tactical_config_save); -} - diff --git a/spaghetti-monster/anduril/tactical-mode.h b/spaghetti-monster/anduril/tactical-mode.h deleted file mode 100644 index 528a796..0000000 --- a/spaghetti-monster/anduril/tactical-mode.h +++ /dev/null @@ -1,22 +0,0 @@ -// tactical-mode.h: Tactical mode for Anduril. -// Copyright (C) 2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#ifndef TACTICAL_LEVELS - // high, low, tactical strobe - // only do color strobe here if it's main LEDs, not aux LEDs - #if defined(USE_POLICE_COLOR_STROBE_MODE) && !defined(POLICE_STROBE_USES_AUX) - // 2-color police style strobe - #define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) - #else - // regular tactical strobe (1 color) - #define TACTICAL_LEVELS 120,30,(RAMP_SIZE+2) - #endif -#endif - -// tactical(ish) mode -uint8_t tactical_state(Event event, uint16_t arg); -uint8_t tactical_config_state(Event event, uint16_t arg); - diff --git a/spaghetti-monster/anduril/tempcheck-mode.c b/spaghetti-monster/anduril/tempcheck-mode.c deleted file mode 100644 index 5d160bd..0000000 --- a/spaghetti-monster/anduril/tempcheck-mode.c +++ /dev/null @@ -1,56 +0,0 @@ -// tempcheck-mode.c: Temperature check mode for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "tempcheck-mode.h" - -uint8_t tempcheck_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: next blinky mode - else if (event == EV_2clicks) { - #if defined(USE_BEACON_MODE) - set_state(beacon_state, 0); - #elif defined(USE_SOS_MODE) && defined(USE_SOS_MODE_IN_BLINKY_GROUP) - set_state(sos_state, 0); - #elif defined(USE_BATTCHECK) - set_state(battcheck_state, 0); - #endif - return EVENT_HANDLED; - } - // 7H: thermal config mode - else if (event == EV_click7_hold) { - push_state(thermal_config_state, 0); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - -void thermal_config_save(uint8_t step, uint8_t value) { - if (value) { - // item 1: calibrate room temperature - if (step == 1) { - int8_t rawtemp = temperature - cfg.therm_cal_offset; - cfg.therm_cal_offset = value - rawtemp; - adc_reset = 2; // invalidate all recent temperature data - } - - // item 2: set maximum heat limit - else { - cfg.therm_ceil = 30 + value - 1; - } - } - - if (cfg.therm_ceil > MAX_THERM_CEIL) cfg.therm_ceil = MAX_THERM_CEIL; -} - -uint8_t thermal_config_state(Event event, uint16_t arg) { - return config_state_base(event, arg, - 2, thermal_config_save); -} - diff --git a/spaghetti-monster/anduril/tempcheck-mode.h b/spaghetti-monster/anduril/tempcheck-mode.h deleted file mode 100644 index 15dd03e..0000000 --- a/spaghetti-monster/anduril/tempcheck-mode.h +++ /dev/null @@ -1,12 +0,0 @@ -// tempcheck-mode.h: Temperature check mode for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#define USE_BLINK_NUM // FIXME: this only matters in an earlier header - -uint8_t tempcheck_state(Event event, uint16_t arg); -uint8_t thermal_config_state(Event event, uint16_t arg); -void thermal_config_save(uint8_t step, uint8_t value); - diff --git a/spaghetti-monster/anduril/tint-ramping.c b/spaghetti-monster/anduril/tint-ramping.c deleted file mode 100644 index 9418113..0000000 --- a/spaghetti-monster/anduril/tint-ramping.c +++ /dev/null @@ -1,86 +0,0 @@ -// tint-ramping.c: Tint ramping functions for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "tint-ramping.h" - -uint8_t tint_ramping_state(Event event, uint16_t arg) { - static int8_t tint_ramp_direction = 1; - static uint8_t prev_tint = 0; - // don't activate auto-tint modes unless the user hits the edge - // and keeps pressing for a while - static uint8_t past_edge_counter = 0; - // bugfix: click-click-hold from off to strobes would invoke tint ramping - // in addition to changing state... so ignore any tint-ramp events which - // don't look like they were meant to be here - static uint8_t active = 0; - - // click, click, hold: change the tint - if (event == EV_click3_hold) { - ///// tint-toggle mode - // toggle once on first frame; ignore other frames - if (tint_style) { - // only respond on first frame - if (arg) return EVENT_NOT_HANDLED; - - // force tint to be 1 or 254 - if (tint != 254) { tint = 1; } - // invert between 1 and 254 - tint = tint ^ 0xFF; - set_level(actual_level); - return EVENT_HANDLED; - } - - ///// smooth tint-ramp mode - // reset at beginning of movement - if (! arg) { - active = 1; // first frame means this is for us - past_edge_counter = 0; // doesn't start until user hits the edge - } - // ignore event if we weren't the ones who handled the first frame - if (! active) return EVENT_HANDLED; - - // change normal tints - if ((tint_ramp_direction > 0) && (tint < 254)) { - tint += 1; - } - else if ((tint_ramp_direction < 0) && (tint > 1)) { - tint -= 1; - } - // if the user kept pressing long enough, go the final step - if (past_edge_counter == 64) { - past_edge_counter ++; - tint ^= 1; // 0 -> 1, 254 -> 255 - blip(); - } - // if tint change stalled, let user know we hit the edge - else if (prev_tint == tint) { - if (past_edge_counter == 0) blip(); - // count up but don't wrap back to zero - if (past_edge_counter < 255) past_edge_counter ++; - } - prev_tint = tint; - set_level(actual_level); - return EVENT_HANDLED; - } - - // click, click, hold, release: reverse direction for next ramp - else if (event == EV_click3_hold_release) { - active = 0; // ignore next hold if it wasn't meant for us - // reverse - tint_ramp_direction = -tint_ramp_direction; - if (tint <= 1) tint_ramp_direction = 1; - else if (tint >= 254) tint_ramp_direction = -1; - // remember tint after battery change - save_config(); - // bug?: for some reason, brightness can seemingly change - // from 1/150 to 2/150 without this next line... not sure why - set_level(actual_level); - return EVENT_HANDLED; - } - - return EVENT_NOT_HANDLED; -} - diff --git a/spaghetti-monster/anduril/tint-ramping.h b/spaghetti-monster/anduril/tint-ramping.h deleted file mode 100644 index 19b8dde..0000000 --- a/spaghetti-monster/anduril/tint-ramping.h +++ /dev/null @@ -1,21 +0,0 @@ -// tint-ramping.h: Tint ramping functions for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -// 0: smooth tint ramp -// 1: toggle tint only between two extremes -#ifdef TINT_RAMP_TOGGLE_ONLY -uint8_t tint_style = 1; -#else -uint8_t tint_style = 0; -#endif - -#ifdef USE_MANUAL_MEMORY -uint8_t manual_memory_tint; -#endif - -// not actually a mode, more of a fallback under other modes -uint8_t tint_ramping_state(Event event, uint16_t arg); - diff --git a/spaghetti-monster/anduril/version-check-mode.c b/spaghetti-monster/anduril/version-check-mode.c deleted file mode 100644 index a47706f..0000000 --- a/spaghetti-monster/anduril/version-check-mode.c +++ /dev/null @@ -1,31 +0,0 @@ -// version-check-mode.c: Version check mode for Anduril. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "version-check-mode.h" - -// empty state; logic is handled in FSM loop() instead -uint8_t version_check_state(Event event, uint16_t arg) { - return EVENT_NOT_HANDLED; -} - -// this happens in FSM loop() -inline void version_check_iter() { - for (uint8_t i=0; i. - */ - -#include "hwdef-Emisar_D4.h" -#define USE_LVP -#define USE_THERMAL_REGULATION -#define USE_DELAY_MS -#include "spaghetti-monster.h" - -// FSM states -uint8_t off_state(Event event, uint16_t arg); -uint8_t steady_state(Event event, uint16_t arg); -uint8_t lockout_state(Event event, uint16_t arg); - -// brightness control -uint8_t memorized_level = 1; -uint8_t actual_level = 0; -#ifdef USE_THERMAL_REGULATION -uint8_t target_level = 0; -#endif - -// moon + ../../bin/level_calc.py 2 6 7135 18 10 150 FET 1 10 1500 -uint8_t pwm1_levels[] = { 3, 18, 110, 255, 255, 255, 0, }; -uint8_t pwm2_levels[] = { 0, 0, 0, 9, 58, 138, 255, }; -#define MAX_LEVEL (sizeof(pwm1_levels)-1) - -// set LED brightness -void set_level(uint8_t lvl) { - actual_level = lvl; - PWM1_LVL = pwm1_levels[lvl]; - PWM2_LVL = pwm2_levels[lvl]; -} - -uint8_t off_state(Event event, uint16_t arg) { - // turn emitter off when entering state - if (event == EV_enter_state) { - go_to_standby = 1; // sleep while off (lower power use) - return EVENT_HANDLED; - } - // hold (initially): go to lowest level, but allow abort for regular click - else if (event == EV_click1_press) { - set_level(0); - return EVENT_HANDLED; - } - // hold (longer): go to lowest level - else if (event == EV_click1_hold) { - set_state(steady_state, 0); - return EVENT_HANDLED; - } - // 1 click (before timeout): go to memorized level, but allow abort for double click - else if (event == EV_click1_release) { - set_level(memorized_level); - return EVENT_HANDLED; - } - // 1 click: regular mode - else if (event == EV_1click) { - set_state(steady_state, memorized_level); - return EVENT_HANDLED; - } - // 2 clicks: highest mode - else if (event == EV_2clicks) { - set_state(steady_state, MAX_LEVEL); - return EVENT_HANDLED; - } - // 4 clicks: soft lockout - else if (event == EV_4clicks) { - set_state(lockout_state, 0); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - -uint8_t steady_state(Event event, uint16_t arg) { - // turn LED on when we first enter the mode - if (event == EV_enter_state) { - // remember this level, unless it's moon or turbo - if ((arg > 0) && (arg < MAX_LEVEL)) memorized_level = arg; - // use the requested level even if not memorized - #ifdef USE_THERMAL_REGULATION - target_level = arg; - #endif - set_level(arg); - return EVENT_HANDLED; - } - // 1 click: off - else if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: go to/from highest level - else if (event == EV_2clicks) { - if (actual_level < MAX_LEVEL) { // go to turbo - memorized_level = actual_level; // in case we're on moon - #ifdef USE_THERMAL_REGULATION - target_level = MAX_LEVEL; - #endif - set_level(MAX_LEVEL); - } - else { // return from turbo - #ifdef USE_THERMAL_REGULATION - target_level = memorized_level; - #endif - set_level(memorized_level); - } - return EVENT_HANDLED; - } - // hold: change brightness - else if (event == EV_click1_hold) { - if ((arg % HOLD_TIMEOUT) == 0) { - memorized_level = (actual_level+1) % (MAX_LEVEL+1); - #ifdef USE_THERMAL_REGULATION - target_level = memorized_level; - #endif - set_level(memorized_level); - } - return EVENT_HANDLED; - } - #ifdef USE_THERMAL_REGULATION - // overheating: drop by 1 level - else if (event == EV_temperature_high) { - if (actual_level > 1) { set_level(actual_level - 1); } - return EVENT_HANDLED; - } - // underheating: increase by 1 level if we're lower than the target - else if (event == EV_temperature_low) { - if (actual_level < target_level) { set_level(actual_level + 1); } - return EVENT_HANDLED; - } - #endif - return EVENT_NOT_HANDLED; -} - -uint8_t lockout_state(Event event, uint16_t arg) { - // stay asleep while locked - if (event == EV_tick) { - PWM1_LVL = 0; PWM2_LVL = 0; // make sure emitters are off - // sleep 1 second after user stops pressing buttons - if (arg > TICKS_PER_SECOND) { go_to_standby = 1; } - return EVENT_HANDLED; - } - // 4 clicks: exit, and turn on at "low" level - else if (event == EV_4clicks) { - set_state(steady_state, 1); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - -void low_voltage() { - // step down by one level or turn off - if (actual_level > 0) { - set_level(actual_level - 1); - #ifdef USE_THERMAL_REGULATION - target_level = actual_level; // don't let low temperature override LVP - #endif - } - else { - set_state(off_state, 0); - } -} - -void setup() { - // blink when power is connected - set_level(MAX_LEVEL/2); - delay_ms(10); - set_level(0); - - push_state(off_state, 0); -} - -void loop() { -} diff --git a/spaghetti-monster/baton/baton.txt b/spaghetti-monster/baton/baton.txt deleted file mode 100644 index 2f0c22f..0000000 --- a/spaghetti-monster/baton/baton.txt +++ /dev/null @@ -1,21 +0,0 @@ -This is a very simple clone of the Olight Baton interface. It is not -exact, but it has the basics. Mostly, it exists for the purposes of -demonstrating how to create interfaces in FSM. - -While off: - - - 1 click: Turn on (at memorized level). - - Hold: Turn on (at moon level). - - 2 clicks: Turn on (at highest level). - - 4 clicks: Soft lockout mode. - -While on: - - - 1 click: Turn off. - - Hold: Change the brightness. Goes up in steps, then wraps around. - - 2 clicks: Go to/from highest level. - -While locked: - - - 4 clicks: Exit lockout mode. - diff --git a/spaghetti-monster/chan-aux.c b/spaghetti-monster/chan-aux.c deleted file mode 100644 index e04e6a2..0000000 --- a/spaghetti-monster/chan-aux.c +++ /dev/null @@ -1,11 +0,0 @@ -// channel modes for single color aux LEDs -// Copyright (C) 2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -void set_level_aux(uint8_t level) { - indicator_led(!(!(level)) << 1); // high (or off) -} - -bool gradual_tick_null(uint8_t gt) { return true; } // do nothing - diff --git a/spaghetti-monster/chan-aux.h b/spaghetti-monster/chan-aux.h deleted file mode 100644 index ff599b8..0000000 --- a/spaghetti-monster/chan-aux.h +++ /dev/null @@ -1,25 +0,0 @@ -// channel modes for single color aux LEDs -// Copyright (C) 2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define NUM_AUX_CHANNEL_MODES 1 - -// include / exclude field based on compile options -#ifdef USE_CHANNEL_MODE_ARGS - #define AUX_HAS_ARGS , .has_args = 0 -#else - #define AUX_HAS_ARGS -#endif - -#define AUX_CHANNELS \ - { \ - .set_level = set_level_aux, \ - .gradual_tick = gradual_tick_null \ - AUX_HAS_ARGS \ - } - -void set_level_aux(uint8_t level); - -bool gradual_tick_null(uint8_t gt); - diff --git a/spaghetti-monster/chan-rgbaux.c b/spaghetti-monster/chan-rgbaux.c deleted file mode 100644 index 19d18a6..0000000 --- a/spaghetti-monster/chan-rgbaux.c +++ /dev/null @@ -1,35 +0,0 @@ -// channel modes for RGB aux LEDs -// Copyright (C) 2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -void set_level_auxred(uint8_t level) { - rgb_led_set(!(!(level)) * 0b000010); // red, high (or off) -} - -void set_level_auxyel(uint8_t level) { - rgb_led_set(!(!(level)) * 0b001010); // red+green, high (or off) -} - -void set_level_auxgrn(uint8_t level) { - rgb_led_set(!(!(level)) * 0b001000); // green, high (or off) -} - -void set_level_auxcyn(uint8_t level) { - rgb_led_set(!(!(level)) * 0b101000); // green+blue, high (or off) -} - -void set_level_auxblu(uint8_t level) { - rgb_led_set(!(!(level)) * 0b100000); // blue, high (or off) -} - -void set_level_auxprp(uint8_t level) { - rgb_led_set(!(!(level)) * 0b100010); // red+blue, high (or off) -} - -void set_level_auxwht(uint8_t level) { - rgb_led_set(!(!(level)) * 0b101010); // red+green+blue, high (or off) -} - -bool gradual_tick_null(uint8_t gt) { return true; } // do nothing - diff --git a/spaghetti-monster/chan-rgbaux.h b/spaghetti-monster/chan-rgbaux.h deleted file mode 100644 index 6ef5d89..0000000 --- a/spaghetti-monster/chan-rgbaux.h +++ /dev/null @@ -1,72 +0,0 @@ -// channel modes for RGB aux LEDs -// Copyright (C) 2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later -#pragma once - -#define RGB_AUX_ENUMS \ - CM_AUXRED, \ - CM_AUXYEL, \ - CM_AUXGRN, \ - CM_AUXCYN, \ - CM_AUXBLU, \ - CM_AUXPRP, \ - CM_AUXWHT - -#define RGB_AUX_CM_ARGS 0,0,0,0,0,0,0 - -#define NUM_RGB_AUX_CHANNEL_MODES 7 - -// include / exclude field based on compile options -#ifdef USE_CHANNEL_MODE_ARGS - #define AUX_RGB_HAS_ARGS , .has_args = 0 -#else - #define AUX_RGB_HAS_ARGS -#endif - -#define RGB_AUX_CHANNELS \ - { \ - .set_level = set_level_auxred, \ - .gradual_tick = gradual_tick_null \ - AUX_RGB_HAS_ARGS \ - }, \ - { \ - .set_level = set_level_auxyel, \ - .gradual_tick = gradual_tick_null \ - AUX_RGB_HAS_ARGS \ - }, \ - { \ - .set_level = set_level_auxgrn, \ - .gradual_tick = gradual_tick_null \ - AUX_RGB_HAS_ARGS \ - }, \ - { \ - .set_level = set_level_auxcyn, \ - .gradual_tick = gradual_tick_null \ - AUX_RGB_HAS_ARGS \ - }, \ - { \ - .set_level = set_level_auxblu, \ - .gradual_tick = gradual_tick_null \ - AUX_RGB_HAS_ARGS \ - }, \ - { \ - .set_level = set_level_auxprp, \ - .gradual_tick = gradual_tick_null \ - AUX_RGB_HAS_ARGS \ - }, \ - { \ - .set_level = set_level_auxwht, \ - .gradual_tick = gradual_tick_null \ - AUX_RGB_HAS_ARGS \ - } - -void set_level_auxred(uint8_t level); -void set_level_auxyel(uint8_t level); -void set_level_auxgrn(uint8_t level); -void set_level_auxcyn(uint8_t level); -void set_level_auxblu(uint8_t level); -void set_level_auxprp(uint8_t level); -void set_level_auxwht(uint8_t level); - -bool gradual_tick_null(uint8_t gt); - diff --git a/spaghetti-monster/darkhorse/darkhorse.c b/spaghetti-monster/darkhorse/darkhorse.c deleted file mode 100644 index aa37b92..0000000 --- a/spaghetti-monster/darkhorse/darkhorse.c +++ /dev/null @@ -1,367 +0,0 @@ -/* - * DarkHorse: Improved ZebraLight clone UI for SpaghettiMonster. - * - * Copyright (C) 2017 Selene ToyKeeper - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "hwdef-Emisar_D4.h" -#define USE_LVP -#define USE_THERMAL_REGULATION -#define DEFAULT_THERM_CEIL 45 -#define USE_RAMPING -#define RAMP_LENGTH 150 -#define USE_BATTCHECK -#define BATTCHECK_4bars -#define DONT_DELAY_AFTER_BATTCHECK -#define USE_EEPROM -#define EEPROM_BYTES 5 -#include "spaghetti-monster.h" - -// FSM states -uint8_t off_state(Event event, uint16_t arg); -uint8_t low_mode_state(Event event, uint16_t arg); -uint8_t med_mode_state(Event event, uint16_t arg); -uint8_t hi_mode_state(Event event, uint16_t arg); -uint8_t strobe_beacon_state(Event event, uint16_t arg); -#ifdef USE_BATTCHECK -uint8_t battcheck_state(Event event, uint16_t arg); -#endif -// Not a FSM state, just handles stuff common to all low/med/hi states -uint8_t any_mode_state(Event event, uint16_t arg, uint8_t *primary, uint8_t *secondary, uint8_t *modes); - -void load_config(); -void save_config(); - -// toggle between L1/L2, M1/M2, H1/H2 -uint8_t L1 = 1; -uint8_t M1 = 1; -uint8_t H1 = 1; -// brightness for L2, M2, H2 (valid range 1 to 3 inclusive) -uint8_t L2 = 1; -uint8_t M2 = 1; -uint8_t H2 = 1; -// mode groups, ish -uint8_t low_modes[] = {12, 3, 5, 9}; // 3.3 lm, 2.0 lm, 0.8 lm, 0.3 lm -uint8_t med_modes[] = {56, 21, 29, 37}; // 101 lm, 35 lm, 20 lm, 10 lm -uint8_t hi_modes[] = {MAX_LEVEL, 81, 96, 113}; // 1500 lm, 678 lm, 430 lm, 270 lm -// strobe/beacon modes: -// 0: 0.2 Hz beacon at L1 -// 1: 0.2 Hz beacon at H1 -// 2: 4 Hz strobe at H1 -// 3: 19 Hz strobe at H1 -uint8_t strobe_beacon_mode = 0; - -#ifdef USE_THERMAL_REGULATION -// brightness before thermal step-down -uint8_t target_level = 0; -#endif - -void set_any_mode(uint8_t primary, uint8_t secondary, uint8_t *modes) { - // primary (H1/M1/L1) - if (primary) { - set_level(modes[0]); - } - // secondary (H2/M2/L2) - else { - set_level(modes[secondary]); - } - #ifdef USE_THERMAL_REGULATION - target_level = actual_level; - #endif -} - -inline void set_low_mode() { set_any_mode(L1, L2, low_modes); } -inline void set_med_mode() { set_any_mode(M1, M2, med_modes); } -inline void set_hi_mode() { set_any_mode(H1, H2, hi_modes); } - - -uint8_t off_state(Event event, uint16_t arg) { - // turn emitter off when entering state - if (event == EV_enter_state) { - set_level(0); - // sleep while off (lower power use) - go_to_standby = 1; - return EVENT_HANDLED; - } - // hold (initially): go to lowest level, but allow abort for regular click - else if (event == EV_click1_press) { - set_low_mode(); - return EVENT_HANDLED; - } - // 1 click (before timeout): go to high level, but allow abort for double click - else if (event == EV_click1_release) { - set_hi_mode(); - return EVENT_HANDLED; - } - // 1 click: high mode - else if (event == EV_1click) { - set_state(hi_mode_state, 0); - return EVENT_HANDLED; - } - // click, press (initially): go to medium mode, but allow abort - else if (event == EV_click2_press) { - set_med_mode(); - return EVENT_HANDLED; - } - // 2 clicks: medium mode - else if (event == EV_2clicks) { - set_state(med_mode_state, 0); - return EVENT_HANDLED; - } - // click, click, press (initially): light off, prep for blinkies - else if (event == EV_click3_press) { - set_level(0); - return EVENT_HANDLED; - } - // 3 clicks: strobe mode - else if (event == EV_3clicks) { - set_state(strobe_beacon_state, 0); - return EVENT_HANDLED; - } - #ifdef USE_BATTCHECK - // 4 clicks: battcheck mode - else if (event == EV_4clicks) { - set_state(battcheck_state, 0); - return EVENT_HANDLED; - } - #endif - // hold: go to low mode, but allow ramping up - else if (event == EV_click1_hold) { - // don't start ramping immediately; - // give the user time to release at low mode - if (arg >= HOLD_TIMEOUT) - set_state(low_mode_state, 0); - return EVENT_HANDLED; - } - // hold, release quickly: go to low mode - else if (event == EV_click1_hold_release) { - set_state(low_mode_state, 0); - return EVENT_HANDLED; - } - /* TODO: implement - // click-release-hold: discrete ramp through all levels - else if (event == EV_click2_hold) { - set_state(steady_state, MAX_LEVEL); - return EVENT_HANDLED; - } - */ - return EVENT_NOT_HANDLED; -} - - -uint8_t any_mode_state(Event event, uint16_t arg, uint8_t *primary, uint8_t *secondary, uint8_t *modes) { - // turn on LED when entering the mode - if (event == EV_enter_state) { - set_any_mode(*primary, *secondary, modes); - return EVENT_HANDLED; - } - // 1 click: off - else if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // hold: change brightness (low, med, hi, always starting at low) - else if (event == EV_click1_hold) { - uint8_t which = arg % (HOLD_TIMEOUT * 3) / HOLD_TIMEOUT; - switch(which) { - case 0: - set_state(low_mode_state, 0); - break; - case 1: - set_state(med_mode_state, 0); - break; - case 2: - set_state(hi_mode_state, 0); - break; - } - return EVENT_HANDLED; - } - // 2 clicks: toggle primary/secondary level - else if (event == EV_2clicks) { - *primary ^= 1; - set_any_mode(*primary, *secondary, modes); - save_config(); - return EVENT_HANDLED; - } - // click-release-hold: change secondary level - else if (event == EV_click2_hold) { - if (arg % HOLD_TIMEOUT == 0) { - *secondary = (*secondary + 1) & 3; - if (! *secondary) *secondary = 1; - *primary = 0; - set_any_mode(*primary, *secondary, modes); - } - return EVENT_HANDLED; - } - // click, hold, release: save secondary level - else if (event == EV_click2_hold_release) { - save_config(); - } - #ifdef USE_THERMAL_REGULATION - // TODO: test this on a real light - // overheating: drop by an amount proportional to how far we are above the ceiling - else if (event == EV_temperature_high) { - if (actual_level > MAX_LEVEL/4) { - uint8_t stepdown = actual_level - arg; - if (stepdown < MAX_LEVEL/4) stepdown = MAX_LEVEL/4; - set_level(stepdown); - } - return EVENT_HANDLED; - } - // underheating: increase slowly if we're lower than the target - // (proportional to how low we are) - else if (event == EV_temperature_low) { - if (actual_level < target_level) { - uint8_t stepup = actual_level + (arg>>1); - if (stepup > target_level) stepup = target_level; - set_level(stepup); - } - return EVENT_HANDLED; - } - #endif - return EVENT_NOT_HANDLED; -} - -uint8_t low_mode_state(Event event, uint16_t arg) { - return any_mode_state(event, arg, &L1, &L2, low_modes); -} - -uint8_t med_mode_state(Event event, uint16_t arg) { - return any_mode_state(event, arg, &M1, &M2, med_modes); -} - -uint8_t hi_mode_state(Event event, uint16_t arg) { - return any_mode_state(event, arg, &H1, &H2, hi_modes); -} - - -#ifdef USE_BATTCHECK -uint8_t battcheck_state(Event event, uint16_t arg) { - return EVENT_NOT_HANDLED; -} -#endif - - -uint8_t strobe_beacon_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 1 click (initially): cancel current blink - // FIXME: this is no longer necessary; FSM does this automatically now - if (event == EV_click1_release) { - interrupt_nice_delays(); - return EVENT_HANDLED; - } - // 2 clicks: rotate through blinky modes - else if (event == EV_2clicks) { - strobe_beacon_mode = (strobe_beacon_mode + 1) & 3; - save_config(); - interrupt_nice_delays(); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - - -void low_voltage() { - if (current_state == hi_mode_state) { - set_state(med_mode_state, 0); - } - else if (current_state == med_mode_state) { - set_state(low_mode_state, 0); - } - else if (current_state == low_mode_state) { - set_state(off_state, 0); - } - // "step down" from blinkies to low - else if (current_state == strobe_beacon_state) { - set_state(low_mode_state, 0); - } -} - -void strobe(uint8_t level, uint16_t ontime, uint16_t offtime) { - set_level(level); - if (! nice_delay_ms(ontime)) return; - set_level(0); - nice_delay_ms(offtime); -} - -void load_config() { - if (load_eeprom()) { - H1 = !(!(eeprom[0] & 0b00000100)); - M1 = !(!(eeprom[0] & 0b00000010)); - L1 = !(!(eeprom[0] & 0b00000001)); - H2 = eeprom[1]; - M2 = eeprom[2]; - L2 = eeprom[3]; - strobe_beacon_mode = eeprom[4]; - } -} - -void save_config() { - eeprom[0] = (H1<<2) | (M1<<1) | (L1); - eeprom[1] = H2; - eeprom[2] = M2; - eeprom[3] = L2; - eeprom[4] = strobe_beacon_mode; - - save_eeprom(); -} - -void setup() { - set_level(RAMP_SIZE/8); - delay_4ms(3); - set_level(0); - - load_config(); - - push_state(off_state, 0); -} - -void loop() { - if (current_state == strobe_beacon_state) { - switch(strobe_beacon_mode) { - // 0.2 Hz beacon at L1 - case 0: - strobe(low_modes[0], 500, 4500); - break; - // 0.2 Hz beacon at H1 - case 1: - strobe(hi_modes[0], 500, 4500); - break; - // 4 Hz tactical strobe at H1 - case 2: - strobe(hi_modes[0], 83, 167); - break; - // 19 Hz tactical strobe at H1 - case 3: - strobe(hi_modes[0], 17, 35); - break; - } - } - - #ifdef USE_BATTCHECK - else if (current_state == battcheck_state) { - nice_delay_ms(500); // wait a moment to measure voltage - battcheck(); - set_state(off_state, 0); - } - #endif -} - - diff --git a/spaghetti-monster/fireflies-ui/Makefile b/spaghetti-monster/fireflies-ui/Makefile deleted file mode 100644 index 0b59898..0000000 --- a/spaghetti-monster/fireflies-ui/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -all: - ./build-all.sh - -clean: - rm -f *.hex cfg-ff-[pr]*.h *~ *.elf *.o - -.phony: clean diff --git a/spaghetti-monster/fireflies-ui/build-all.sh b/spaghetti-monster/fireflies-ui/build-all.sh deleted file mode 100755 index 81ebd97..0000000 --- a/spaghetti-monster/fireflies-ui/build-all.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -cp -av --no-clobber ../anduril/cfg-ff*.h . - -UI=fireflies-ui - -for TARGET in cfg-*.h ; do - NAME=$(echo "$TARGET" | perl -ne '/cfg-(.*).h/ && print "$1\n";') - echo "===== $NAME =====" - echo ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}" - ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}" - mv -f "$UI".hex "$UI".$NAME.hex -done diff --git a/spaghetti-monster/fireflies-ui/cfg-ff-e01.h b/spaghetti-monster/fireflies-ui/cfg-ff-e01.h deleted file mode 100644 index 42c23b2..0000000 --- a/spaghetti-monster/fireflies-ui/cfg-ff-e01.h +++ /dev/null @@ -1,44 +0,0 @@ -// Fireflies EDC thrower config options for Fireflies UI -// (uses PL47 driver) -#include "hwdef-FF_PL47.h" - -// disable indicator LED; it's hardwired -#ifdef USE_INDICATOR_LED -#undef USE_INDICATOR_LED -#endif - -// don't do this -#undef BLINK_AT_RAMP_MIDDLE -#undef BLINK_AT_RAMP_CEILING - -// ramp shape and size -#define RAMP_LENGTH 150 - -// driver is a FET + 3x7135, ~413 lm at highest regulated level -// ../../../bin/level_calc.py seventh 2 150 7135 1 12 414 FET 2 10 1930 -#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12,13,14,15,15,16,17,18,20,21,22,23,24,26,27,28,30,31,33,34,36,38,39,41,43,45,47,49,51,53,56,58,60,63,65,68,71,74,77,80,83,86,89,93,96,100,103,107,111,115,119,124,128,132,137,142,147,152,157,163,168,174,180,186,192,198,204,211,218,225,232,240,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 -#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,7,9,12,14,16,19,22,24,27,30,33,36,39,42,45,48,52,55,58,62,66,69,73,77,81,85,90,94,98,103,107,112,117,122,127,132,137,143,148,154,160,166,172,178,184,191,197,204,211,218,225,232,240,247,255 -#define MAX_1x7135 93 -#define HALFSPEED_LEVEL 14 -#define QUARTERSPEED_LEVEL 7 - -#define MIN_THERM_STEPDOWN 65 // lowest value it'll step down to - - -// ceiling is level 130/150 (50% power) -#define RAMP_SMOOTH_FLOOR 1 -#define RAMP_SMOOTH_CEIL 130 - -// 20, 56, [93], 130 (93 is highest regulated) -// (8 / 102 / 413 / 1163 + 1930 lm) -#define RAMP_DISCRETE_FLOOR 20 -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#define RAMP_DISCRETE_STEPS 4 - -// ~25 lm to ~400 lm -#define MUGGLE_FLOOR 30 -#define MUGGLE_CEILING MAX_1x7135 - -// throttle back faster when high -#define THERM_FASTER_LEVEL 130 - diff --git a/spaghetti-monster/fireflies-ui/cfg-ff-e07-2.h b/spaghetti-monster/fireflies-ui/cfg-ff-e07-2.h deleted file mode 100644 index 48f9c15..0000000 --- a/spaghetti-monster/fireflies-ui/cfg-ff-e07-2.h +++ /dev/null @@ -1,27 +0,0 @@ -// Fireflies E07-2 config options for Anduril / FFUI -// mostly the same as PL47 -#include "cfg-ff-pl47.h" - -// ceiling is level 130/150 (50% power) -#undef RAMP_SMOOTH_CEIL -#define RAMP_SMOOTH_CEIL 130 - -// 20, 56, 93, 130 (83 is highest regulated) -// (requested config is 1%, 5%, 25%, 50%, double-click-turbo) -// (but this doesn't allow us to hit level 83) -#undef RAMP_DISCRETE_FLOOR -#define RAMP_DISCRETE_FLOOR 20 -#undef RAMP_DISCRETE_CEIL -#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#undef RAMP_DISCRETE_STEPS -#define RAMP_DISCRETE_STEPS 4 - -// regulate down faster when the FET is active, slower otherwise -#undef THERM_FASTER_LEVEL -#define THERM_FASTER_LEVEL 130 // throttle back faster when high - -// play it safe, don't try to regulate above the recommended safe level -#ifndef THERM_HARD_TURBO_DROP -#define THERM_HARD_TURBO_DROP -#endif - diff --git a/spaghetti-monster/fireflies-ui/fireflies-ui.c b/spaghetti-monster/fireflies-ui/fireflies-ui.c deleted file mode 100644 index 34f8293..0000000 --- a/spaghetti-monster/fireflies-ui/fireflies-ui.c +++ /dev/null @@ -1,2386 +0,0 @@ -/* - * Fireflies UI: A custom UI for Fireflies-brand flashlights. - * (based on Anduril by ToyKeeper) - * - * Copyright (C) 2019 Selene ToyKeeper - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/********* User-configurable options *********/ -// UI config file name (set it here or define it at the gcc command line) -//#define CONFIGFILE cfg-ff-pl47.h - -#define USE_LVP // FIXME: won't build when this option is turned off - -// parameters for this defined below or per-driver -#define USE_THERMAL_REGULATION -#define DEFAULT_THERM_CEIL 45 // try not to get hotter than this - -// short blip when crossing from "click" to "hold" from off -// (helps the user hit moon mode exactly, instead of holding too long -// or too short) -#define MOON_TIMING_HINT -// short blips while ramping -#define BLINK_AT_RAMP_MIDDLE -//#define BLINK_AT_RAMP_FLOOR -#define BLINK_AT_RAMP_CEILING -//#define BLINK_AT_STEPS // whenever a discrete ramp mode is passed in smooth mode - -// ramp down via regular button hold if a ramp-up ended <1s ago -// ("hold, release, hold" ramps down instead of up) -#define USE_REVERSING - -// battery readout style (pick one) -#define BATTCHECK_VpT -//#define BATTCHECK_8bars // FIXME: breaks build -//#define BATTCHECK_4bars // FIXME: breaks build - -// enable/disable various strobe modes -#define USE_BIKE_FLASHER_MODE -#define USE_PARTY_STROBE_MODE -#define USE_TACTICAL_STROBE_MODE -#define USE_LIGHTNING_MODE -#define USE_CANDLE_MODE - -// enable sunset (goodnight) mode -#define USE_GOODNIGHT_MODE -#define GOODNIGHT_TIME 60 // minutes (approximately) -#define GOODNIGHT_LEVEL 24 // ~11 lm - -// enable beacon mode -#define USE_BEACON_MODE - -//Muggle mode for easy UI -#define USE_MUGGLE_MODE - -// make the ramps configurable by the user -#define USE_RAMP_CONFIG - -// boring strobes nobody really likes, but sometimes flashlight companies want -// (these replace the fun strobe group, -// so don't enable them at the same time as any of the above strobes) -//#define USE_POLICE_STROBE_MODE -//#define USE_SOS_MODE - -// dual-switch support (second switch is a tail clicky) -//#define START_AT_MEMORIZED_LEVEL - -/***** specific settings for known driver types *****/ -#include "tk.h" -#include incfile(CONFIGFILE) - -///// Fireflies-specific configuration -// disable ramp config -#ifdef USE_RAMP_CONFIG -#undef USE_RAMP_CONFIG -#endif - -// no muggle mode -#ifdef USE_MUGGLE_MODE -#undef USE_MUGGLE_MODE -#endif - -// turn off strobe mode entirely; we're replacing it -#ifdef USE_BIKE_FLASHER_MODE -#undef USE_BIKE_FLASHER_MODE -#endif -#ifdef USE_PARTY_STROBE_MODE -#undef USE_PARTY_STROBE_MODE -#endif -#ifdef USE_TACTICAL_STROBE_MODE -#undef USE_TACTICAL_STROBE_MODE -#endif -#ifdef USE_LIGHTNING_MODE -#undef USE_LIGHTNING_MODE -#endif -#ifdef USE_CANDLE_MODE -#undef USE_CANDLE_MODE -#endif - -// remove other blinkies too -#ifdef USE_GOODNIGHT_MODE -#undef USE_GOODNIGHT_MODE -#endif -#ifdef USE_BEACON_MODE -#undef USE_BEACON_MODE -#endif - -// use these strobes instead -#define USE_POLICE_STROBE_MODE -#define USE_SOS_MODE - -// thermal config mode on 10 clicks from off -#define USE_TENCLICK_THERMAL_CONFIG - -///// end Fireflies-specific configuration - -// thermal properties, if not defined per-driver -#ifndef MIN_THERM_STEPDOWN -#define MIN_THERM_STEPDOWN MAX_1x7135 // lowest value it'll step down to -#endif -#ifndef THERM_FASTER_LEVEL - #ifdef MAX_Nx7135 - #define THERM_FASTER_LEVEL MAX_Nx7135 // throttle back faster when high - #else - #define THERM_FASTER_LEVEL (RAMP_SIZE*4/5) // throttle back faster when high - #endif -#endif -#ifdef USE_THERMAL_REGULATION -#define USE_SET_LEVEL_GRADUALLY // isn't used except for thermal adjustments -#endif - - -/********* Configure SpaghettiMonster *********/ -#define USE_DELAY_ZERO -#define USE_RAMPING -#ifndef RAMP_LENGTH -#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file -#endif -#define MAX_BIKING_LEVEL 120 // should be 127 or less -#define USE_BATTCHECK - -#if defined(USE_MUGGLE_MODE) -#ifndef MUGGLE_FLOOR -#define MUGGLE_FLOOR 22 -#endif -#ifndef MUGGLE_CEILING -#define MUGGLE_CEILING (MAX_1x7135+20) -#endif -#endif -#define USE_IDLE_MODE // reduce power use while awake and no tasks are pending -#define USE_DYNAMIC_UNDERCLOCKING // cut clock speed at very low modes for better efficiency - -// full FET strobe can be a bit much... use max regulated level instead, -// if there's a bright enough regulated level -#ifdef MAX_Nx7135 -#define STROBE_BRIGHTNESS MAX_Nx7135 -#else -#define STROBE_BRIGHTNESS MAX_LEVEL -#endif - -#if defined(USE_CANDLE_MODE) || defined(USE_BIKE_FLASHER_MODE) || defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) || defined(USE_LIGHTNING_MODE) -#define USE_STROBE_STATE -#endif - -#if defined(USE_POLICE_STROBE_MODE) || defined(USE_SOS_MODE) -#define USE_BORING_STROBE_STATE -#endif - -// auto-detect how many eeprom bytes -#define USE_EEPROM -typedef enum { - ramp_style_e, - #ifdef USE_RAMP_CONFIG - ramp_smooth_floor_e, - ramp_smooth_ceil_e, - ramp_discrete_floor_e, - ramp_discrete_ceil_e, - ramp_discrete_steps_e, - #endif - #ifdef USE_TINT_RAMPING - tint_e, - #endif - #ifdef USE_STROBE_STATE - strobe_type_e, - #endif - #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) - strobe_delays_0_e, - strobe_delays_1_e, - #endif - #ifdef USE_BIKE_FLASHER_MODE - bike_flasher_brightness_e, - #endif - #ifdef USE_BEACON_MODE - beacon_seconds_e, - #endif - #ifdef USE_MUGGLE_MODE - muggle_mode_active_e, - #endif - #ifdef USE_THERMAL_REGULATION - therm_ceil_e, - therm_cal_offset_e, - #endif - #ifdef USE_INDICATOR_LED - indicator_led_mode_e, - #endif - eeprom_indexes_e_END -} eeprom_indexes_e; -#define EEPROM_BYTES eeprom_indexes_e_END - -#ifdef START_AT_MEMORIZED_LEVEL -#define USE_EEPROM_WL -#define EEPROM_WL_BYTES 1 -#endif - -// auto-configure other stuff... -#if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE) -#define USE_PSEUDO_RAND -#endif - -#if defined(USE_CANDLE_MODE) -#ifndef USE_TRIANGLE_WAVE -#define USE_TRIANGLE_WAVE -#endif -#endif - -#include "spaghetti-monster.h" - - -// FSM states -uint8_t off_state(Event event, uint16_t arg); -// simple numeric entry config menu -uint8_t config_state_base(Event event, uint16_t arg, - uint8_t num_config_steps, - void (*savefunc)()); -#define MAX_CONFIG_VALUES 3 -uint8_t config_state_values[MAX_CONFIG_VALUES]; -// ramping mode and its related config mode -uint8_t steady_state(Event event, uint16_t arg); -#ifdef USE_RAMP_CONFIG -uint8_t ramp_config_state(Event event, uint16_t arg); -#endif -#ifdef USE_TINT_RAMPING -// not actually a mode, more of a fallback under other modes -uint8_t tint_ramping_state(Event event, uint16_t arg); -#endif -// party and tactical strobes -#ifdef USE_STROBE_STATE -uint8_t strobe_state(Event event, uint16_t arg); -#endif -#ifdef USE_BORING_STROBE_STATE -uint8_t boring_strobe_state(Event event, uint16_t arg); -volatile uint8_t boring_strobe_type = 0; -void sos_blink(uint8_t num, uint8_t dah); -#define strobe_state boring_strobe_state // use the right strobes -#define NUM_BORING_STROBES 2 -#endif -#ifdef USE_BATTCHECK -uint8_t battcheck_state(Event event, uint16_t arg); -#endif -#ifdef USE_THERMAL_REGULATION -uint8_t tempcheck_state(Event event, uint16_t arg); -uint8_t thermal_config_state(Event event, uint16_t arg); -#endif -#ifdef USE_GOODNIGHT_MODE -// 1-hour ramp down from low, then automatic off -uint8_t goodnight_state(Event event, uint16_t arg); -#endif -#ifdef USE_BEACON_MODE -// beacon mode and its related config mode -uint8_t beacon_state(Event event, uint16_t arg); -uint8_t beacon_config_state(Event event, uint16_t arg); -#endif -// soft lockout -#define MOON_DURING_LOCKOUT_MODE -// if enabled, 2nd lockout click goes to the other ramp's floor level -#define LOCKOUT_MOON_FANCY -uint8_t lockout_state(Event event, uint16_t arg); -// momentary / signalling mode -uint8_t momentary_state(Event event, uint16_t arg); -uint8_t momentary_mode = 0; // 0 = ramping, 1 = strobe -uint8_t momentary_active = 0; // boolean, true if active *right now* -#ifdef USE_MUGGLE_MODE -// muggle mode, super-simple, hard to exit -uint8_t muggle_state(Event event, uint16_t arg); -uint8_t muggle_mode_active = 0; -#endif - -// general helper function for config modes -uint8_t number_entry_state(Event event, uint16_t arg); -// return value from number_entry_state() -volatile uint8_t number_entry_value; - -void blink_confirm(uint8_t num); -void blip(); -#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY) -void indicator_blink(uint8_t arg); -#endif - -// remember stuff even after battery was changed -void load_config(); -void save_config(); -#ifdef START_AT_MEMORIZED_LEVEL -void save_config_wl(); -#endif - -// default ramp options if not overridden earlier per-driver -#ifndef RAMP_STYLE -#define RAMP_STYLE 0 // smooth default -#endif -#ifndef RAMP_SMOOTH_FLOOR - #define RAMP_SMOOTH_FLOOR 1 -#endif -#ifndef RAMP_SMOOTH_CEIL - #if PWM_CHANNELS == 3 - #define RAMP_SMOOTH_CEIL MAX_Nx7135 - #else - #define RAMP_SMOOTH_CEIL MAX_LEVEL - 30 - #endif -#endif -#ifndef RAMP_DISCRETE_FLOOR - #define RAMP_DISCRETE_FLOOR 20 -#endif -#ifndef RAMP_DISCRETE_CEIL - #define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#endif -#ifndef RAMP_DISCRETE_STEPS - #define RAMP_DISCRETE_STEPS 7 -#endif - -// mile marker(s) partway up the ramp -// default: blink only at border between regulated and FET -#ifdef BLINK_AT_RAMP_MIDDLE - #if PWM_CHANNELS >= 3 - #ifndef BLINK_AT_RAMP_MIDDLE_1 - #define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135 - #ifndef BLINK_AT_RAMP_MIDDLE_2 - #define BLINK_AT_RAMP_MIDDLE_2 MAX_1x7135 - #endif - #endif - #else - #ifndef BLINK_AT_RAMP_MIDDLE_1 - #define BLINK_AT_RAMP_MIDDLE_1 MAX_1x7135 - #endif - #endif -#endif - -// brightness control -#ifndef DEFAULT_LEVEL -#define DEFAULT_LEVEL MAX_1x7135 -#endif -uint8_t memorized_level = DEFAULT_LEVEL; -// smooth vs discrete ramping -volatile uint8_t ramp_style = RAMP_STYLE; // 0 = smooth, 1 = discrete -volatile uint8_t ramp_smooth_floor = RAMP_SMOOTH_FLOOR; -volatile uint8_t ramp_smooth_ceil = RAMP_SMOOTH_CEIL; -volatile uint8_t ramp_discrete_floor = RAMP_DISCRETE_FLOOR; -volatile uint8_t ramp_discrete_ceil = RAMP_DISCRETE_CEIL; -volatile uint8_t ramp_discrete_steps = RAMP_DISCRETE_STEPS; -uint8_t ramp_discrete_step_size; // don't set this - -#ifdef USE_INDICATOR_LED - // bits 2-3 control lockout mode - // bits 0-1 control "off" mode - // modes are: 0=off, 1=low, 2=high, 3=blinking (if TICK_DURING_STANDBY enabled) - #ifdef INDICATOR_LED_DEFAULT_MODE - uint8_t indicator_led_mode = INDICATOR_LED_DEFAULT_MODE; - #else - #ifdef USE_INDICATOR_LED_WHILE_RAMPING - //uint8_t indicator_led_mode = (1<<2) + 2; - uint8_t indicator_led_mode = (2<<2) + 1; - #else - uint8_t indicator_led_mode = (3<<2) + 1; - #endif - #endif -#endif - -// calculate the nearest ramp level which would be valid at the moment -// (is a no-op for smooth ramp, but limits discrete ramp to only the -// correct levels for the user's config) -uint8_t nearest_level(int16_t target); - -#ifdef USE_THERMAL_REGULATION -// brightness before thermal step-down -uint8_t target_level = 0; -#endif - -// internal numbering for strobe modes -#ifdef USE_STROBE_STATE -typedef enum { - #ifdef USE_PARTY_STROBE_MODE - party_strobe_e, - #endif - #ifdef USE_TACTICAL_STROBE_MODE - tactical_strobe_e, - #endif - #ifdef USE_LIGHTNING_MODE - lightning_storm_e, - #endif - #ifdef USE_CANDLE_MODE - candle_mode_e, - #endif - #ifdef USE_BIKE_FLASHER_MODE - bike_flasher_e, - #endif - strobe_mode_END -} strobe_mode_te; - -const int NUM_STROBES = strobe_mode_END; - -// which strobe mode is active? -#ifdef USE_CANDLE_MODE -volatile strobe_mode_te strobe_type = candle_mode_e; -#else -volatile strobe_mode_te strobe_type = 0; -#endif -#endif - -#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) -// party / tactical strobe timing -volatile uint8_t strobe_delays[] = { 40, 67 }; // party strobe, tactical strobe -#endif - -// bike mode config options -#ifdef USE_BIKE_FLASHER_MODE -volatile uint8_t bike_flasher_brightness = MAX_1x7135; -#endif - -#ifdef USE_CANDLE_MODE -uint8_t candle_mode_state(Event event, uint16_t arg); -uint8_t triangle_wave(uint8_t phase); -#ifndef CANDLE_AMPLITUDE -#define CANDLE_AMPLITUDE 25 -#endif -#endif - -#ifdef USE_BEACON_MODE -// beacon timing -volatile uint8_t beacon_seconds = 2; -#endif - - -uint8_t off_state(Event event, uint16_t arg) { - // turn emitter off when entering state - if (event == EV_enter_state) { - set_level(0); - #ifdef USE_INDICATOR_LED - indicator_led(indicator_led_mode & 0x03); - #endif - // sleep while off (lower power use) - go_to_standby = 1; - return EVENT_HANDLED; - } - // go back to sleep eventually if we got bumped but didn't leave "off" state - else if (event == EV_tick) { - if (arg > TICKS_PER_SECOND*2) { - go_to_standby = 1; - #ifdef USE_INDICATOR_LED - indicator_led(indicator_led_mode & 0x03); - #endif - } - return EVENT_HANDLED; - } - #if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED) - // blink the indicator LED, maybe - else if (event == EV_sleep_tick) { - if ((indicator_led_mode & 0b00000011) == 0b00000011) { - indicator_blink(arg); - } - return EVENT_HANDLED; - } - #endif - // hold (initially): go to lowest level (floor), but allow abort for regular click - else if (event == EV_click1_press) { - set_level(nearest_level(1)); - return EVENT_HANDLED; - } - // hold: go to lowest level - else if (event == EV_click1_hold) { - #ifdef MOON_TIMING_HINT - if (arg == 0) { - // let the user know they can let go now to stay at moon - blip(); - } else - #endif - // don't start ramping immediately; - // give the user time to release at moon level - //if (arg >= HOLD_TIMEOUT) { // smaller - if (arg >= (!ramp_style) * HOLD_TIMEOUT) { // more consistent - set_state(steady_state, 1); - } - return EVENT_HANDLED; - } - // hold, release quickly: go to lowest level (floor) - else if (event == EV_click1_hold_release) { - set_state(steady_state, 1); - return EVENT_HANDLED; - } - // 1 click (before timeout): go to memorized level, but allow abort for double click - else if (event == EV_click1_release) { - set_level(nearest_level(memorized_level)); - return EVENT_HANDLED; - } - // 1 click: regular mode - else if (event == EV_1click) { - set_state(steady_state, memorized_level); - return EVENT_HANDLED; - } - // click, hold: go to highest level (ceiling) (for ramping down) - else if (event == EV_click2_hold) { - set_state(steady_state, MAX_LEVEL); - return EVENT_HANDLED; - } - // 2 clicks: highest mode (ceiling) - else if (event == EV_2clicks) { - set_state(steady_state, MAX_LEVEL); - return EVENT_HANDLED; - } - // 3 clicks (initial press): off, to prep for later events - else if (event == EV_click3_press) { - set_level(0); - return EVENT_HANDLED; - } - #ifdef USE_BATTCHECK - // 3 clicks: battcheck mode / blinky mode group 1 - else if (event == EV_3clicks) { - set_state(battcheck_state, 0); - return EVENT_HANDLED; - } - #endif - // click, click, long-click: strobe mode - #ifdef USE_STROBE_STATE - else if (event == EV_click3_hold) { - set_state(strobe_state, 0); - return EVENT_HANDLED; - } - #elif defined(USE_BORING_STROBE_STATE) - else if (event == EV_click3_hold) { - set_state(boring_strobe_state, 0); - return EVENT_HANDLED; - } - #endif - // 4 clicks: soft lockout - else if (event == EV_4clicks) { - blink_confirm(2); - set_state(lockout_state, 0); - return EVENT_HANDLED; - } - // 5 clicks: momentary mode - else if (event == EV_5clicks) { - blink_confirm(1); - set_state(momentary_state, 0); - return EVENT_HANDLED; - } - #ifdef USE_MUGGLE_MODE - // 6 clicks: muggle mode - else if (event == EV_6clicks) { - blink_confirm(1); - set_state(muggle_state, 0); - return EVENT_HANDLED; - } - #endif - #ifdef USE_INDICATOR_LED - // 7 clicks: change indicator LED mode - else if (event == EV_7clicks) { - uint8_t mode = (indicator_led_mode & 3) + 1; - #ifdef TICK_DURING_STANDBY - mode = mode & 3; - #else - mode = mode % 3; - #endif - #ifdef INDICATOR_LED_SKIP_LOW - if (mode == 1) { mode ++; } - #endif - indicator_led_mode = (indicator_led_mode & 0b11111100) | mode; - indicator_led(mode); - save_config(); - return EVENT_HANDLED; - } - #endif - // 8 clicks: temperature check - else if (event == EV_8clicks) { - set_state(tempcheck_state, 0); - return EVENT_HANDLED; - } - #ifdef USE_TENCLICK_THERMAL_CONFIG - // 10 clicks: thermal config mode - else if (event == EV_10clicks) { - push_state(thermal_config_state, 0); - return EVENT_HANDLED; - } - #endif - return EVENT_NOT_HANDLED; -} - - -uint8_t steady_state(Event event, uint16_t arg) { - uint8_t mode_min = ramp_smooth_floor; - uint8_t mode_max = ramp_smooth_ceil; - uint8_t ramp_step_size = 1; - #ifdef USE_REVERSING - static int8_t ramp_direction = 1; - #endif - if (ramp_style) { - mode_min = ramp_discrete_floor; - mode_max = ramp_discrete_ceil; - ramp_step_size = ramp_discrete_step_size; - } - - // turn LED on when we first enter the mode - if ((event == EV_enter_state) || (event == EV_reenter_state)) { - momentary_mode = 0; // 0 = ramping, 1 = strobes - // if we just got back from config mode, go back to memorized level - if (event == EV_reenter_state) { - arg = memorized_level; - } - // remember this level, unless it's moon or turbo - if ((arg > mode_min) && (arg < mode_max)) - memorized_level = arg; - // use the requested level even if not memorized - arg = nearest_level(arg); - #ifdef USE_THERMAL_REGULATION - target_level = arg; - #endif - set_level(arg); - #ifdef USE_REVERSING - ramp_direction = 1; - #endif - return EVENT_HANDLED; - } - // 1 click: off - else if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: go to/from highest level - else if (event == EV_2clicks) { - if (actual_level < MAX_LEVEL) { - #ifdef USE_THERMAL_REGULATION - target_level = MAX_LEVEL; - #endif - // true turbo, not the mode-specific ceiling - set_level(MAX_LEVEL); - } - else { - #ifdef USE_THERMAL_REGULATION - target_level = memorized_level; - #endif - set_level(memorized_level); - } - return EVENT_HANDLED; - } - // 3 clicks: toggle smooth vs discrete ramping - else if (event == EV_3clicks) { - ramp_style = !ramp_style; - memorized_level = nearest_level(actual_level); - #ifdef USE_THERMAL_REGULATION - target_level = memorized_level; - #ifdef USE_SET_LEVEL_GRADUALLY - //set_level_gradually(lvl); - #endif - #endif - save_config(); - #ifdef START_AT_MEMORIZED_LEVEL - save_config_wl(); - #endif - blip(); - set_level(memorized_level); - return EVENT_HANDLED; - } - #ifdef USE_RAMP_CONFIG - // 4 clicks: configure this ramp mode - else if (event == EV_4clicks) { - push_state(ramp_config_state, 0); - return EVENT_HANDLED; - } - #endif - // hold: change brightness (brighter) - else if (event == EV_click1_hold) { - // ramp slower in discrete mode - if (ramp_style && (arg % HOLD_TIMEOUT != 0)) { - return EVENT_HANDLED; - } - #ifdef USE_REVERSING - // fix ramp direction on first frame if necessary - if (!arg) { - // make it ramp down instead, if already at max - if (actual_level >= mode_max) { ramp_direction = -1; } - // make it ramp up if already at min - // (off->hold->stepped_min->release causes this state) - else if (actual_level <= mode_min) { ramp_direction = 1; } - } - memorized_level = nearest_level((int16_t)actual_level \ - + (ramp_step_size * ramp_direction)); - #else - memorized_level = nearest_level((int16_t)actual_level + ramp_step_size); - #endif - #ifdef USE_THERMAL_REGULATION - target_level = memorized_level; - #endif - #if defined(BLINK_AT_RAMP_CEILING) || defined(BLINK_AT_RAMP_MIDDLE) - // only blink once for each threshold - if ((memorized_level != actual_level) && ( - 0 // for easier syntax below - #ifdef BLINK_AT_RAMP_MIDDLE_1 - || (memorized_level == BLINK_AT_RAMP_MIDDLE_1) - #endif - #ifdef BLINK_AT_RAMP_MIDDLE_2 - || (memorized_level == BLINK_AT_RAMP_MIDDLE_2) - #endif - #ifdef BLINK_AT_RAMP_CEILING - || (memorized_level == mode_max) - #endif - #if defined(USE_REVERSING) && defined(BLINK_AT_RAMP_FLOOR) - || (memorized_level == mode_min) - #endif - )) { - blip(); - } - #endif - #if defined(BLINK_AT_STEPS) - uint8_t foo = ramp_style; - ramp_style = 1; - uint8_t nearest = nearest_level((int16_t)actual_level); - ramp_style = foo; - // only blink once for each threshold - if ((memorized_level != actual_level) && - (ramp_style == 0) && - (memorized_level == nearest) - ) - { - blip(); - } - #endif - set_level(memorized_level); - return EVENT_HANDLED; - } - #if defined(USE_REVERSING) || defined(START_AT_MEMORIZED_LEVEL) - // reverse ramp direction on hold release - else if (event == EV_click1_hold_release) { - #ifdef USE_REVERSING - ramp_direction = -ramp_direction; - #endif - #ifdef START_AT_MEMORIZED_LEVEL - save_config_wl(); - #endif - return EVENT_HANDLED; - } - #endif - // click, hold: change brightness (dimmer) - else if (event == EV_click2_hold) { - #ifdef USE_REVERSING - ramp_direction = 1; - #endif - // ramp slower in discrete mode - if (ramp_style && (arg % HOLD_TIMEOUT != 0)) { - return EVENT_HANDLED; - } - // TODO? make it ramp up instead, if already at min? - memorized_level = nearest_level((int16_t)actual_level - ramp_step_size); - #ifdef USE_THERMAL_REGULATION - target_level = memorized_level; - #endif - #if defined(BLINK_AT_RAMP_FLOOR) || defined(BLINK_AT_RAMP_MIDDLE) - // only blink once for each threshold - if ((memorized_level != actual_level) && ( - 0 // for easier syntax below - #ifdef BLINK_AT_RAMP_MIDDLE_1 - || (memorized_level == BLINK_AT_RAMP_MIDDLE_1) - #endif - #ifdef BLINK_AT_RAMP_MIDDLE_2 - || (memorized_level == BLINK_AT_RAMP_MIDDLE_2) - #endif - #ifdef BLINK_AT_RAMP_FLOOR - || (memorized_level == mode_min) - #endif - )) { - blip(); - } - #endif - #if defined(BLINK_AT_STEPS) - uint8_t foo = ramp_style; - ramp_style = 1; - uint8_t nearest = nearest_level((int16_t)actual_level); - ramp_style = foo; - // only blink once for each threshold - if ((memorized_level != actual_level) && - (ramp_style == 0) && - (memorized_level == nearest) - ) - { - blip(); - } - #endif - set_level(memorized_level); - return EVENT_HANDLED; - } - #ifdef START_AT_MEMORIZED_LEVEL - // click, release, hold, release: save new ramp level (if necessary) - else if (event == EV_click2_hold_release) { - save_config_wl(); - return EVENT_HANDLED; - } - #endif - #if defined(USE_SET_LEVEL_GRADUALLY) || defined(USE_REVERSING) - else if (event == EV_tick) { - #ifdef USE_REVERSING - // un-reverse after 1 second - if (arg == TICKS_PER_SECOND) ramp_direction = 1; - #endif - #ifdef USE_SET_LEVEL_GRADUALLY - // make thermal adjustment speed scale with magnitude - if ((arg & 1) && (actual_level < THERM_FASTER_LEVEL)) { - return EVENT_HANDLED; // adjust slower when not a high mode - } - #ifdef THERM_HARD_TURBO_DROP - else if ((! (actual_level < THERM_FASTER_LEVEL)) - && (actual_level > gradual_target)) { - gradual_tick(); - } - else { - #endif - // [int(62*4 / (x**0.8)) for x in (1,2,4,8,16,32,64,128)] - //uint8_t intervals[] = {248, 142, 81, 46, 26, 15, 8, 5}; - // [int(62*4 / (x**0.9)) for x in (1,2,4,8,16,32,64,128)] - //uint8_t intervals[] = {248, 132, 71, 38, 20, 10, 5, 3}; - // [int(62*4 / (x**0.95)) for x in (1,2,4,8,16,32,64,128)] - uint8_t intervals[] = {248, 128, 66, 34, 17, 9, 4, 2}; - uint8_t diff; - static uint8_t ticks_since_adjust = 0; - ticks_since_adjust ++; - if (gradual_target > actual_level) diff = gradual_target - actual_level; - else { - diff = actual_level - gradual_target; - } - uint8_t magnitude = 0; - #ifndef THERM_HARD_TURBO_DROP - // if we're on a really high mode, drop faster - if (actual_level >= THERM_FASTER_LEVEL) { magnitude ++; } - #endif - while (diff) { - magnitude ++; - diff >>= 1; - } - uint8_t ticks_per_adjust = intervals[magnitude]; - if (ticks_since_adjust > ticks_per_adjust) - { - gradual_tick(); - ticks_since_adjust = 0; - } - //if (!(arg % ticks_per_adjust)) gradual_tick(); - #ifdef THERM_HARD_TURBO_DROP - } - #endif - #endif - return EVENT_HANDLED; - } - #endif - #ifdef USE_THERMAL_REGULATION - // overheating: drop by an amount proportional to how far we are above the ceiling - else if (event == EV_temperature_high) { - #if 0 - blip(); - #endif - #ifdef THERM_HARD_TURBO_DROP - if (actual_level > THERM_FASTER_LEVEL) { - #ifdef USE_SET_LEVEL_GRADUALLY - set_level_gradually(THERM_FASTER_LEVEL); - #else - set_level(THERM_FASTER_LEVEL); - #endif - target_level = THERM_FASTER_LEVEL; - } else - #endif - if (actual_level > MIN_THERM_STEPDOWN) { - int16_t stepdown = actual_level - arg; - if (stepdown < MIN_THERM_STEPDOWN) stepdown = MIN_THERM_STEPDOWN; - else if (stepdown > MAX_LEVEL) stepdown = MAX_LEVEL; - #ifdef USE_SET_LEVEL_GRADUALLY - set_level_gradually(stepdown); - #else - set_level(stepdown); - #endif - } - return EVENT_HANDLED; - } - // underheating: increase slowly if we're lower than the target - // (proportional to how low we are) - else if (event == EV_temperature_low) { - #if 0 - blip(); - #endif - if (actual_level < target_level) { - //int16_t stepup = actual_level + (arg>>1); - int16_t stepup = actual_level + arg; - if (stepup > target_level) stepup = target_level; - else if (stepup < MIN_THERM_STEPDOWN) stepup = MIN_THERM_STEPDOWN; - #ifdef USE_SET_LEVEL_GRADUALLY - set_level_gradually(stepup); - #else - set_level(stepup); - #endif - } - return EVENT_HANDLED; - } - #endif - return EVENT_NOT_HANDLED; -} - - -#ifdef USE_TINT_RAMPING -uint8_t tint_ramping_state(Event event, uint16_t arg) { - static int8_t tint_ramp_direction = 1; - static uint8_t prev_tint = 0; - // don't activate auto-tint modes unless the user hits the edge - // and keeps pressing for a while - static uint8_t past_edge_counter = 0; - // bugfix: click-click-hold from off to strobes would invoke tint ramping - // in addition to changing state... so ignore any tint-ramp events which - // don't look like they were meant to be here - static uint8_t active = 0; - - // click, click, hold: change the tint - if (event == EV_click3_hold) { - // reset at beginning of movement - if (! arg) { - active = 1; // first frame means this is for us - past_edge_counter = 0; // doesn't start until user hits the edge - } - // ignore event if we weren't the ones who handled the first frame - if (! active) return EVENT_HANDLED; - - // change normal tints - if ((tint_ramp_direction > 0) && (tint < 254)) { - tint += 1; - } - else if ((tint_ramp_direction < 0) && (tint > 1)) { - tint -= 1; - } - // if the user kept pressing long enough, go the final step - if (past_edge_counter == 64) { - past_edge_counter ++; - tint ^= 1; // 0 -> 1, 254 -> 255 - blip(); - } - // if tint change stalled, let user know we hit the edge - else if (prev_tint == tint) { - if (past_edge_counter == 0) blip(); - // count up but don't wrap back to zero - if (past_edge_counter < 255) past_edge_counter ++; - } - prev_tint = tint; - set_level(actual_level); - return EVENT_HANDLED; - } - - // click, click, hold, release: reverse direction for next ramp - else if (event == EV_click3_hold_release) { - active = 0; // ignore next hold if it wasn't meant for us - // reverse - tint_ramp_direction = -tint_ramp_direction; - if (tint == 0) tint_ramp_direction = 1; - else if (tint == 255) tint_ramp_direction = -1; - // remember tint after battery change - save_config(); - return EVENT_HANDLED; - } - - return EVENT_NOT_HANDLED; -} -#endif // ifdef USE_TINT_RAMPING - - -#ifdef USE_STROBE_STATE -uint8_t strobe_state(Event event, uint16_t arg) { - static int8_t ramp_direction = 1; - - // 'st' reduces ROM size by avoiding access to a volatile var - // (maybe I should just make it nonvolatile?) - strobe_mode_te st = strobe_type; - - momentary_mode = 1; // 0 = ramping, 1 = strobes - - #ifdef USE_CANDLE_MODE - // pass all events to candle mode, when it's active - // (the code is in its own pseudo-state to keep things cleaner) - if (st == candle_mode_e) { - candle_mode_state(event, arg); - } - #endif - - if (0) {} // placeholder - // init anything which needs to be initialized - else if (event == EV_enter_state) { - ramp_direction = 1; - return EVENT_HANDLED; - } - // 1 click: off - else if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: rotate through strobe/flasher modes - else if (event == EV_2clicks) { - strobe_type = (st + 1) % NUM_STROBES; - save_config(); - return EVENT_HANDLED; - } - // hold: change speed (go faster) - // or change brightness (brighter) - else if (event == EV_click1_hold) { - if (0) {} // placeholder - - // party / tactical strobe faster - #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) - #ifdef USE_TACTICAL_STROBE_MODE - else if (st <= tactical_strobe_e) { - #else - else if (st == party_strobe_e) { - #endif - if ((arg & 1) == 0) { - uint8_t d = strobe_delays[st]; - d -= ramp_direction; - if (d < 8) d = 8; - else if (d > 254) d = 254; - strobe_delays[st] = d; - } - } - #endif - - // lightning has no adjustments - //else if (st == lightning_storm_e) {} - - // biking mode brighter - #ifdef USE_BIKE_FLASHER_MODE - else if (st == bike_flasher_e) { - bike_flasher_brightness += ramp_direction; - if (bike_flasher_brightness < 2) bike_flasher_brightness = 2; - else if (bike_flasher_brightness > MAX_BIKING_LEVEL) bike_flasher_brightness = MAX_BIKING_LEVEL; - set_level(bike_flasher_brightness); - } - #endif - - return EVENT_HANDLED; - } - // reverse ramp direction on hold release - // ... and save new strobe settings - else if (event == EV_click1_hold_release) { - ramp_direction = -ramp_direction; - save_config(); - return EVENT_HANDLED; - } - // click, hold: change speed (go slower) - // or change brightness (dimmer) - else if (event == EV_click2_hold) { - ramp_direction = 1; - - if (0) {} // placeholder - - // party / tactical strobe slower - #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) - #ifdef USE_TACTICAL_STROBE_MODE - else if (st <= tactical_strobe_e) { - #else - else if (st == party_strobe_e) { - #endif - if ((arg & 1) == 0) { - if (strobe_delays[st] < 255) strobe_delays[st] ++; - } - } - #endif - - // lightning has no adjustments - //else if (st == lightning_storm_e) {} - - // biking mode dimmer - #ifdef USE_BIKE_FLASHER_MODE - else if (st == bike_flasher_e) { - if (bike_flasher_brightness > 2) - bike_flasher_brightness --; - set_level(bike_flasher_brightness); - } - #endif - - return EVENT_HANDLED; - } - // release hold: save new strobe settings - else if (event == EV_click2_hold_release) { - save_config(); - return EVENT_HANDLED; - } - #if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE) - // clock tick: bump the random seed - else if (event == EV_tick) { - // un-reverse after 1 second - if (arg == TICKS_PER_SECOND) ramp_direction = 1; - - pseudo_rand_seed += arg; - return EVENT_HANDLED; - } - #endif - return EVENT_NOT_HANDLED; -} - -#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) -inline void party_tactical_strobe_mode_iter(uint8_t st) { - // one iteration of main loop() - uint8_t del = strobe_delays[st]; - // TODO: make tac strobe brightness configurable? - set_level(STROBE_BRIGHTNESS); - if (0) {} // placeholde0 - #ifdef USE_PARTY_STROBE_MODE - else if (st == party_strobe_e) { // party strobe - if (del < 42) delay_zero(); - else nice_delay_ms(1); - } - #endif - #ifdef USE_TACTICAL_STROBE_MODE - else { //tactical strobe - nice_delay_ms(del >> 1); - } - #endif - set_level(0); - nice_delay_ms(del); // no return check necessary on final delay -} -#endif - -#ifdef USE_LIGHTNING_MODE -inline void lightning_storm_iter() { - // one iteration of main loop() - int16_t brightness; - uint16_t rand_time; - - // turn the emitter on at a random level, - // for a random amount of time between 1ms and 32ms - //rand_time = 1 << (pseudo_rand() % 7); - rand_time = pseudo_rand() & 63; - brightness = 1 << (pseudo_rand() % 7); // 1, 2, 4, 8, 16, 32, 64 - brightness += 1 << (pseudo_rand() % 5); // 2 to 80 now - brightness += pseudo_rand() % brightness; // 2 to 159 now (w/ low bias) - if (brightness > MAX_LEVEL) brightness = MAX_LEVEL; - set_level(brightness); - nice_delay_ms(rand_time); - - // decrease the brightness somewhat more gradually, like lightning - uint8_t stepdown = brightness >> 3; - if (stepdown < 1) stepdown = 1; - while(brightness > 1) { - nice_delay_ms(rand_time); - brightness -= stepdown; - if (brightness < 0) brightness = 0; - set_level(brightness); - /* - if ((brightness < MAX_LEVEL/2) && (! (pseudo_rand() & 15))) { - brightness <<= 1; - set_level(brightness); - } - */ - if (! (pseudo_rand() & 3)) { - nice_delay_ms(rand_time); - set_level(brightness>>1); - } - } - - // turn the emitter off, - // for a random amount of time between 1ms and 8192ms - // (with a low bias) - rand_time = 1 << (pseudo_rand() % 13); - rand_time += pseudo_rand() % rand_time; - set_level(0); - nice_delay_ms(rand_time); // no return check necessary on final delay -} -#endif - -#ifdef USE_BIKE_FLASHER_MODE -inline void bike_flasher_iter() { - // one iteration of main loop() - uint8_t burst = bike_flasher_brightness << 1; - if (burst > MAX_LEVEL) burst = MAX_LEVEL; - for(uint8_t i=0; i<4; i++) { - set_level(burst); - nice_delay_ms(5); - set_level(bike_flasher_brightness); - nice_delay_ms(65); - } - nice_delay_ms(720); // no return check necessary on final delay -} -#endif - -#endif // ifdef USE_STROBE_STATE - -#ifdef USE_CANDLE_MODE -uint8_t candle_mode_state(Event event, uint16_t arg) { - static int8_t ramp_direction = 1; - #define MAX_CANDLE_LEVEL (RAMP_LENGTH-CANDLE_AMPLITUDE-15) - static uint8_t candle_wave1 = 0; - static uint8_t candle_wave2 = 0; - static uint8_t candle_wave3 = 0; - static uint8_t candle_wave2_speed = 0; - // these should add up to 100 - #define CANDLE_WAVE1_MAXDEPTH 30 - #define CANDLE_WAVE2_MAXDEPTH 45 - #define CANDLE_WAVE3_MAXDEPTH 25 - static const uint8_t candle_wave1_depth = CANDLE_WAVE1_MAXDEPTH * CANDLE_AMPLITUDE / 100; - static uint8_t candle_wave2_depth = CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100; - static uint8_t candle_wave3_depth = CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100; - static uint8_t candle_mode_brightness = 24; - static uint8_t candle_mode_timer = 0; - #define TICKS_PER_CANDLE_MINUTE 4096 // about 65 seconds - #define MINUTES_PER_CANDLE_HALFHOUR 27 // ish - - if (event == EV_enter_state) { - candle_mode_timer = 0; // in case any time was left over from earlier - ramp_direction = 1; - return EVENT_HANDLED; - } - // 2 clicks: cancel timer - else if (event == EV_2clicks) { - // parent state just rotated through strobe/flasher modes, - // so cancel timer... in case any time was left over from earlier - candle_mode_timer = 0; - return EVENT_HANDLED; - } - // hold: change brightness (brighter) - else if (event == EV_click1_hold) { - // ramp away from extremes - if (! arg) { - if (candle_mode_brightness >= MAX_CANDLE_LEVEL) { ramp_direction = -1; } - else if (candle_mode_brightness <= 1) { ramp_direction = 1; } - } - // change brightness, but not too far - candle_mode_brightness += ramp_direction; - if (candle_mode_brightness < 1) candle_mode_brightness = 1; - else if (candle_mode_brightness > MAX_CANDLE_LEVEL) candle_mode_brightness = MAX_CANDLE_LEVEL; - return EVENT_HANDLED; - } - // reverse ramp direction on hold release - else if (event == EV_click1_hold_release) { - ramp_direction = -ramp_direction; - return EVENT_HANDLED; - } - // click, hold: change brightness (dimmer) - else if (event == EV_click2_hold) { - ramp_direction = 1; - if (candle_mode_brightness > 1) - candle_mode_brightness --; - return EVENT_HANDLED; - } - // 3 clicks: add 30m to candle timer - else if (event == EV_3clicks) { - if (candle_mode_timer < (255 - MINUTES_PER_CANDLE_HALFHOUR)) { - // add 30m to the timer - candle_mode_timer += MINUTES_PER_CANDLE_HALFHOUR; - // blink to confirm - set_level(actual_level + 32); - delay_4ms(2); - } - return EVENT_HANDLED; - } - // clock tick: animate candle brightness - else if (event == EV_tick) { - // un-reverse after 1 second - if (arg == TICKS_PER_SECOND) ramp_direction = 1; - - // self-timer dims the light during the final minute - uint8_t subtract = 0; - if (candle_mode_timer == 1) { - subtract = ((candle_mode_brightness+CANDLE_AMPLITUDE) - * ((arg & (TICKS_PER_CANDLE_MINUTE-1)) >> 4)) - >> 8; - } - // we passed a minute mark, decrease timer if it's running - if ((arg & (TICKS_PER_CANDLE_MINUTE-1)) == (TICKS_PER_CANDLE_MINUTE - 1)) { - if (candle_mode_timer > 0) { - candle_mode_timer --; - //set_level(0); delay_4ms(2); - // if the timer ran out, shut off - if (! candle_mode_timer) { - set_state(off_state, 0); - } - } - } - // 3-oscillator synth for a relatively organic pattern - uint8_t add; - add = ((triangle_wave(candle_wave1) * candle_wave1_depth) >> 8) - + ((triangle_wave(candle_wave2) * candle_wave2_depth) >> 8) - + ((triangle_wave(candle_wave3) * candle_wave3_depth) >> 8); - int8_t brightness = candle_mode_brightness + add - subtract; - if (brightness < 0) { brightness = 0; } - set_level(brightness); - - // wave1: slow random LFO - // TODO: make wave slower and more erratic? - if ((arg & 1) == 0) candle_wave1 += pseudo_rand() & 1; - // wave2: medium-speed erratic LFO - candle_wave2 += candle_wave2_speed; - // wave3: erratic fast wave - candle_wave3 += pseudo_rand() % 37; - // S&H on wave2 frequency to make it more erratic - if ((pseudo_rand() & 0b00111111) == 0) - candle_wave2_speed = pseudo_rand() % 13; - // downward sawtooth on wave2 depth to simulate stabilizing - if ((candle_wave2_depth > 0) && ((pseudo_rand() & 0b00111111) == 0)) - candle_wave2_depth --; - // random sawtooth retrigger - if (pseudo_rand() == 0) { - // random amplitude - //candle_wave2_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2)); - candle_wave2_depth = pseudo_rand() % (CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100); - //candle_wave3_depth = 5; - candle_wave2 = 0; - } - // downward sawtooth on wave3 depth to simulate stabilizing - if ((candle_wave3_depth > 2) && ((pseudo_rand() & 0b00011111) == 0)) - candle_wave3_depth --; - if ((pseudo_rand() & 0b01111111) == 0) - // random amplitude - //candle_wave3_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2)); - candle_wave3_depth = pseudo_rand() % (CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} -#endif // #ifdef USE_CANDLE_MODE - - -#ifdef USE_BORING_STROBE_STATE -uint8_t boring_strobe_state(Event event, uint16_t arg) { - // police strobe and SOS, meh - // 'st' reduces ROM size by avoiding access to a volatile var - // (maybe I should just make it nonvolatile?) - uint8_t st = boring_strobe_type; - - momentary_mode = 1; // 0 = ramping, 1 = strobes - - if (event == EV_enter_state) { - return EVENT_HANDLED; - } - // 1 click: off - else if (event == EV_1click) { - // reset to police strobe for next time - boring_strobe_type = 0; - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: rotate through strobe/flasher modes - else if (event == EV_2clicks) { - boring_strobe_type = (st + 1) % NUM_BORING_STROBES; - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - -#ifdef USE_POLICE_STROBE_MODE -inline void police_strobe_iter() { - // one iteration of main loop() - // flash at 16 Hz then 8 Hz, 8 times each - for (uint8_t del=41; del<100; del+=41) { - for (uint8_t f=0; f<8; f++) { - set_level(STROBE_BRIGHTNESS); - nice_delay_ms(del >> 1); - set_level(0); - nice_delay_ms(del); - } - } -} -#endif - -#ifdef USE_SOS_MODE -void sos_blink(uint8_t num, uint8_t dah) { - #define DIT_LENGTH 200 - for (; num > 0; num--) { - set_level(memorized_level); - nice_delay_ms(DIT_LENGTH); - if (dah) { // dah is 3X as long as a dit - nice_delay_ms(DIT_LENGTH*2); - } - set_level(0); - // one "off" dit between blinks - nice_delay_ms(DIT_LENGTH); - } - // three "off" dits (or one "dah") between letters - nice_delay_ms(DIT_LENGTH*2); -} - -inline void sos_mode_iter() { - // one iteration of main loop() - nice_delay_ms(1000); - sos_blink(3, 0); // S - sos_blink(3, 1); // O - sos_blink(3, 0); // S - nice_delay_ms(1000); -} -#endif // #ifdef USE_SOS_MODE -#endif // #ifdef USE_BORING_STROBE_STATE - - -#ifdef USE_BATTCHECK -uint8_t battcheck_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - #if defined(USE_GOODNIGHT_MODE) || defined(USE_BEACON_MODE) - // 2 clicks: next mode - else if (event == EV_2clicks) { - #ifdef USE_GOODNIGHT_MODE - set_state(goodnight_state, 0); - #elif defined(USE_BEACON_MODE) - set_state(beacon_state, 0); - #endif - return EVENT_HANDLED; - } - #endif - return EVENT_NOT_HANDLED; -} -#endif - - -#ifdef USE_THERMAL_REGULATION -uint8_t tempcheck_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - #if 0 // not part of a loop in this UI - // 2 clicks: battcheck mode - else if (event == EV_2clicks) { - set_state(battcheck_state, 0); - return EVENT_HANDLED; - } - #endif - // 4 clicks: thermal config mode - else if (event == EV_4clicks) { - push_state(thermal_config_state, 0); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} -#endif - - -#ifdef USE_BEACON_MODE -uint8_t beacon_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // TODO: use sleep ticks to measure time between pulses, - // to save power - // 2 clicks: tempcheck mode - else if (event == EV_2clicks) { - #ifdef USE_THERMAL_REGULATION - set_state(tempcheck_state, 0); - #else - set_state(battcheck_state, 0); - #endif - return EVENT_HANDLED; - } - // 4 clicks: beacon config mode - else if (event == EV_4clicks) { - push_state(beacon_config_state, 0); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} -#endif // #ifdef USE_BEACON_MODE - - -#ifdef USE_GOODNIGHT_MODE -#define GOODNIGHT_TICKS_PER_STEPDOWN (GOODNIGHT_TIME*TICKS_PER_SECOND*60L/GOODNIGHT_LEVEL) -uint8_t goodnight_state(Event event, uint16_t arg) { - static uint16_t ticks_since_stepdown = 0; - // blink on start - if (event == EV_enter_state) { - ticks_since_stepdown = 0; - blink_confirm(2); - set_level(GOODNIGHT_LEVEL); - return EVENT_HANDLED; - } - // 1 click: off - else if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: beacon mode - else if (event == EV_2clicks) { - #ifdef USE_BEACON_MODE - set_state(beacon_state, 0); - #elif defined(USE_TEMPCHECK_MODE) - set_state(tempcheck_state, 0); - #endif - return EVENT_HANDLED; - } - // tick: step down (maybe) or off (maybe) - else if (event == EV_tick) { - if (++ticks_since_stepdown > GOODNIGHT_TICKS_PER_STEPDOWN) { - ticks_since_stepdown = 0; - set_level(actual_level-1); - if (! actual_level) { - #if 0 // test blink, to help measure timing - set_level(MAX_LEVEL>>2); - delay_4ms(8/2); - set_level(0); - #endif - set_state(off_state, 0); - } - } - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} -#endif - - -uint8_t lockout_state(Event event, uint16_t arg) { - #ifdef MOON_DURING_LOCKOUT_MODE - // momentary(ish) moon mode during lockout - // button is being held - if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { - #ifdef LOCKOUT_MOON_LOWEST - // Use lowest moon configured - uint8_t lvl = ramp_smooth_floor; - if (ramp_discrete_floor < lvl) lvl = ramp_discrete_floor; - set_level(lvl); - #elif defined(LOCKOUT_MOON_FANCY) - uint8_t levels[] = { ramp_smooth_floor, ramp_discrete_floor }; - if ((event & 0x0f) == 2) { - set_level(levels[ramp_style^1]); - } else { - set_level(levels[ramp_style]); - } - #else - // Use moon from current ramp - set_level(nearest_level(1)); - #endif - } - // button was released - else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { - set_level(0); - } - #endif - - // regular event handling - // conserve power while locked out - // (allow staying awake long enough to exit, but otherwise - // be persistent about going back to sleep every few seconds - // even if the user keeps pressing the button) - #ifdef USE_INDICATOR_LED - if (event == EV_enter_state) { - indicator_led(indicator_led_mode >> 2); - } else - #endif - if (event == EV_tick) { - if (arg > TICKS_PER_SECOND*2) { - go_to_standby = 1; - #ifdef USE_INDICATOR_LED - indicator_led(indicator_led_mode >> 2); - #endif - } - return EVENT_HANDLED; - } - #if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED) - else if (event == EV_sleep_tick) { - if ((indicator_led_mode & 0b00001100) == 0b00001100) { - indicator_blink(arg); - } - return EVENT_HANDLED; - } - #endif - #ifdef USE_INDICATOR_LED - // 3 clicks: rotate through indicator LED modes (lockout mode) - else if (event == EV_3clicks) { - uint8_t mode = indicator_led_mode >> 2; - #ifdef TICK_DURING_STANDBY - mode = (mode + 1) & 3; - #else - mode = (mode + 1) % 3; - #endif - #ifdef INDICATOR_LED_SKIP_LOW - if (mode == 1) { mode ++; } - #endif - indicator_led_mode = (mode << 2) + (indicator_led_mode & 0x03); - indicator_led(mode); - save_config(); - return EVENT_HANDLED; - } - #if 0 // old method, deprecated in favor of "7 clicks from off" - // click, click, hold: rotate through indicator LED modes (off mode) - else if (event == EV_click3_hold) { - #ifndef USE_INDICATOR_LED_WHILE_RAMPING - // if main LED obscures aux LEDs, turn it off - set_level(0); - #endif - #ifdef TICK_DURING_STANDBY - uint8_t mode = (arg >> 5) & 3; - #else - uint8_t mode = (arg >> 5) % 3; - #endif - #ifdef INDICATOR_LED_SKIP_LOW - if (mode == 1) { mode ++; } - #endif - indicator_led_mode = (indicator_led_mode & 0b11111100) | mode; - #ifdef TICK_DURING_STANDBY - if (mode == 3) - indicator_led(mode & (arg&3)); - else - indicator_led(mode); - #else - indicator_led(mode); - #endif - //save_config(); - return EVENT_HANDLED; - } - // click, click, hold, release: save indicator LED mode (off mode) - else if (event == EV_click3_hold_release) { - save_config(); - return EVENT_HANDLED; - } - #endif - #endif - // 4 clicks: exit - else if (event == EV_4clicks) { - blink_confirm(1); - set_state(off_state, 0); - return EVENT_HANDLED; - } - - return EVENT_NOT_HANDLED; -} - - -uint8_t momentary_state(Event event, uint16_t arg) { - // TODO: momentary strobe here? (for light painting) - - // init strobe mode, if relevant - if ((event == EV_enter_state) && (momentary_mode == 1)) { - strobe_state(event, arg); - } - - // light up when the button is pressed; go dark otherwise - // button is being held - if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { - momentary_active = 1; - // 0 = ramping, 1 = strobes - if (momentary_mode == 0) { - set_level(memorized_level); - } - return EVENT_HANDLED; - } - // button was released - else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { - momentary_active = 0; - set_level(0); - //go_to_standby = 1; // sleep while light is off - return EVENT_HANDLED; - } - - // Sleep, dammit! (but wait a few seconds first) - // (because standby mode uses such little power that it can interfere - // with exiting via tailcap loosen+tighten unless you leave power - // disconnected for several seconds, so we want to be awake when that - // happens to speed up the process) - else if (event == EV_tick) { - if (momentary_active) { - // 0 = ramping, 1 = strobes - if (momentary_mode == 1) { - return strobe_state(event, arg); - } - } - else { - if (arg > TICKS_PER_SECOND*15) { // sleep after 15 seconds - go_to_standby = 1; // sleep while light is off - // TODO: lighted button should use lockout config? - } - } - return EVENT_HANDLED; - } - - return EVENT_NOT_HANDLED; -} - - -#ifdef USE_MUGGLE_MODE -uint8_t muggle_state(Event event, uint16_t arg) { - static int8_t ramp_direction; - static int8_t muggle_off_mode; - - // turn LED off when we first enter the mode - if (event == EV_enter_state) { - ramp_direction = 1; - - #ifdef START_AT_MEMORIZED_LEVEL - memorized_level = arg; - muggle_off_mode = 0; - set_level(memorized_level); - - if (! muggle_mode_active) { // don't write eeprom at every boot - muggle_mode_active = 1; - save_config(); - } - #else - muggle_mode_active = 1; - save_config(); - - muggle_off_mode = 1; - //memorized_level = MAX_1x7135; - memorized_level = (MUGGLE_FLOOR + MUGGLE_CEILING) / 2; - #endif - return EVENT_HANDLED; - } - // initial press: moon hint - else if (event == EV_click1_press) { - if (muggle_off_mode) - set_level(MUGGLE_FLOOR); - } - // initial release: direct to memorized level - else if (event == EV_click1_release) { - if (muggle_off_mode) - set_level(memorized_level); - } - // if the user keeps pressing, turn off - else if (event == EV_click2_press) { - muggle_off_mode = 1; - set_level(0); - } - // 1 click: on/off - else if (event == EV_1click) { - muggle_off_mode ^= 1; - if (muggle_off_mode) { - set_level(0); - } - /* - else { - set_level(memorized_level); - } - */ - return EVENT_HANDLED; - } - // hold: change brightness - else if (event == EV_click1_hold) { - // ramp at half speed - if (arg & 1) return EVENT_HANDLED; - - // if off, start at bottom - if (muggle_off_mode) { - muggle_off_mode = 0; - ramp_direction = 1; - set_level(MUGGLE_FLOOR); - } - else { - uint8_t m; - m = actual_level; - // ramp down if already at ceiling - if ((arg <= 1) && (m >= MUGGLE_CEILING)) ramp_direction = -1; - // ramp - m += ramp_direction; - if (m < MUGGLE_FLOOR) - m = MUGGLE_FLOOR; - if (m > MUGGLE_CEILING) - m = MUGGLE_CEILING; - memorized_level = m; - set_level(m); - } - return EVENT_HANDLED; - } - // reverse ramp direction on hold release - else if (event == EV_click1_hold_release) { - ramp_direction = -ramp_direction; - #ifdef START_AT_MEMORIZED_LEVEL - save_config_wl(); // momentary use should retain brightness level - #endif - return EVENT_HANDLED; - } - /* - // click, hold: change brightness (dimmer) - else if (event == EV_click2_hold) { - ramp_direction = 1; - if (memorized_level > MUGGLE_FLOOR) - memorized_level = actual_level - 1; - set_level(memorized_level); - return EVENT_HANDLED; - } - */ - // 6 clicks: exit muggle mode - else if (event == EV_6clicks) { - blink_confirm(1); - muggle_mode_active = 0; - save_config(); - set_state(off_state, 0); - return EVENT_HANDLED; - } - // tick: housekeeping - else if (event == EV_tick) { - // un-reverse after 1 second - if (arg == TICKS_PER_SECOND) ramp_direction = 1; - - // turn off, but don't go to the main "off" state - if (muggle_off_mode) { - if (arg > TICKS_PER_SECOND*1) { // sleep after 1 second - go_to_standby = 1; // sleep while light is off - } - } - return EVENT_HANDLED; - } - #ifdef USE_THERMAL_REGULATION - // overheating is handled specially in muggle mode - else if(event == EV_temperature_high) { - #if 0 - blip(); - #endif - // step down proportional to the amount of overheating - uint8_t new = actual_level - arg; - if (new < MUGGLE_FLOOR) { new = MUGGLE_FLOOR; } - set_level(new); - return EVENT_HANDLED; - } - #endif - // low voltage is handled specially in muggle mode - else if(event == EV_voltage_low) { - uint8_t lvl = (actual_level >> 1) + (actual_level >> 2); - if (lvl >= MUGGLE_FLOOR) { - set_level(lvl); - } else { - muggle_off_mode = 1; - } - return EVENT_HANDLED; - } - - return EVENT_NOT_HANDLED; -} -#endif - - -// ask the user for a sequence of numbers, then save them and return to caller -uint8_t config_state_base(Event event, uint16_t arg, - uint8_t num_config_steps, - void (*savefunc)()) { - static uint8_t config_step; - if (event == EV_enter_state) { - config_step = 0; - set_level(0); - return EVENT_HANDLED; - } - // advance forward through config steps - else if (event == EV_tick) { - if (config_step < num_config_steps) { - push_state(number_entry_state, config_step + 1); - } - else { - // TODO: blink out some sort of success pattern - savefunc(); - save_config(); - //set_state(retstate, retval); - pop_state(); - } - return EVENT_HANDLED; - } - // an option was set (return from number_entry_state) - else if (event == EV_reenter_state) { - config_state_values[config_step] = number_entry_value; - config_step ++; - return EVENT_HANDLED; - } - //return EVENT_NOT_HANDLED; - // eat all other events; don't pass any through to parent - return EVENT_HANDLED; -} - -#ifdef USE_RAMP_CONFIG -void ramp_config_save() { - // parse values - uint8_t val; - if (ramp_style) { // discrete / stepped ramp - - val = config_state_values[0]; - if (val) { ramp_discrete_floor = val; } - - val = config_state_values[1]; - if (val) { ramp_discrete_ceil = MAX_LEVEL + 1 - val; } - - val = config_state_values[2]; - if (val) ramp_discrete_steps = val; - - } else { // smooth ramp - - val = config_state_values[0]; - if (val) { ramp_smooth_floor = val; } - - val = config_state_values[1]; - if (val) { ramp_smooth_ceil = MAX_LEVEL + 1 - val; } - - } -} - -uint8_t ramp_config_state(Event event, uint16_t arg) { - uint8_t num_config_steps; - num_config_steps = 2 + ramp_style; - return config_state_base(event, arg, - num_config_steps, ramp_config_save); -} -#endif // #ifdef USE_RAMP_CONFIG - - -#ifdef USE_THERMAL_REGULATION -void thermal_config_save() { - // parse values - uint8_t val; - - // calibrate room temperature - val = config_state_values[0]; - if (val) { - int8_t rawtemp = temperature - therm_cal_offset; - therm_cal_offset = val - rawtemp; - reset_thermal_history = 1; // invalidate all recent temperature data - } - - val = config_state_values[1]; - if (val) { - // set maximum heat limit - therm_ceil = 30 + val - 1; - } - if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL; -} - -uint8_t thermal_config_state(Event event, uint16_t arg) { - return config_state_base(event, arg, - 2, thermal_config_save); -} -#endif // #ifdef USE_THERMAL_REGULATION - - -#ifdef USE_BEACON_MODE -void beacon_config_save() { - // parse values - uint8_t val = config_state_values[0]; - if (val) { - beacon_seconds = val; - } -} - -uint8_t beacon_config_state(Event event, uint16_t arg) { - return config_state_base(event, arg, - 1, beacon_config_save); -} - -inline void beacon_mode_iter() { - // one iteration of main loop() - set_level(memorized_level); - nice_delay_ms(100); - set_level(0); - nice_delay_ms(((beacon_seconds) * 1000) - 100); -} -#endif // #ifdef USE_BEACON_MODE - - -uint8_t number_entry_state(Event event, uint16_t arg) { - static uint8_t value; - static uint8_t blinks_left; - static uint8_t entry_step; - static uint16_t wait_ticks; - if (event == EV_enter_state) { - value = 0; - blinks_left = arg; - entry_step = 0; - wait_ticks = 0; - return EVENT_HANDLED; - } - // advance through the process: - // 0: wait a moment - // 1: blink out the 'arg' value - // 2: wait a moment - // 3: "buzz" while counting clicks - // 4: save and exit - else if (event == EV_tick) { - // wait a moment - if ((entry_step == 0) || (entry_step == 2)) { - if (wait_ticks < TICKS_PER_SECOND/2) - wait_ticks ++; - else { - entry_step ++; - wait_ticks = 0; - } - } - // blink out the option number - else if (entry_step == 1) { - if (blinks_left) { - if ((wait_ticks & 31) == 10) { - set_level(RAMP_SIZE/4); - } - else if ((wait_ticks & 31) == 20) { - set_level(0); - } - else if ((wait_ticks & 31) == 31) { - blinks_left --; - } - wait_ticks ++; - } - else { - entry_step ++; - wait_ticks = 0; - } - } - else if (entry_step == 3) { // buzz while waiting for a number to be entered - wait_ticks ++; - // buzz for N seconds after last event - if ((wait_ticks & 3) == 0) { - set_level(RAMP_SIZE/6); - } - else if ((wait_ticks & 3) == 2) { - set_level(RAMP_SIZE/8); - } - // time out after 3 seconds - if (wait_ticks > TICKS_PER_SECOND*3) { - //number_entry_value = value; - set_level(0); - entry_step ++; - } - } - else if (entry_step == 4) { - number_entry_value = value; - pop_state(); - } - return EVENT_HANDLED; - } - // count clicks - else if (event == EV_click1_release) { - empty_event_sequence(); - if (entry_step == 3) { // only count during the "buzz" - value ++; - wait_ticks = 0; - // flash briefly - set_level(RAMP_SIZE/2); - delay_4ms(8/2); - set_level(0); - } - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - - -// find the ramp level closest to the target, -// using only the levels which are allowed in the current state -uint8_t nearest_level(int16_t target) { - // bounds check - // using int16_t here saves us a bunch of logic elsewhere, - // by allowing us to correct for numbers < 0 or > 255 in one central place - uint8_t mode_min = ramp_smooth_floor; - uint8_t mode_max = ramp_smooth_ceil; - if (ramp_style) { - mode_min = ramp_discrete_floor; - mode_max = ramp_discrete_ceil; - } - if (target < mode_min) return mode_min; - if (target > mode_max) return mode_max; - // the rest isn't relevant for smooth ramping - if (! ramp_style) return target; - - uint8_t ramp_range = ramp_discrete_ceil - ramp_discrete_floor; - ramp_discrete_step_size = ramp_range / (ramp_discrete_steps-1); - uint8_t this_level = ramp_discrete_floor; - - for(uint8_t i=0; i>1)) - return this_level; - } - return this_level; -} - - -void blink_confirm(uint8_t num) { - for (; num>0; num--) { - set_level(MAX_LEVEL/4); - delay_4ms(10/4); - set_level(0); - delay_4ms(100/4); - } -} - -// Just go dark for a moment to indicate to user that something happened -void blip() { - uint8_t temp = actual_level; - set_level(0); - delay_4ms(3); - set_level(temp); -} - - -#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY) -// beacon-like mode for the indicator LED -void indicator_blink(uint8_t arg) { - #define USE_FANCIER_BLINKING_INDICATOR - #ifdef USE_FANCIER_BLINKING_INDICATOR - - // fancy blink, set off/low/high levels here: - uint8_t seq[] = {0, 1, 2, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0}; - indicator_led(seq[arg & 15]); - - #else // basic blink, 1/8th duty cycle - - if (! (arg & 7)) { - indicator_led(2); - } - else { - indicator_led(0); - } - - #endif -} -#endif - - -void load_config() { - if (load_eeprom()) { - ramp_style = eeprom[ramp_style_e]; - #ifdef USE_RAMP_CONFIG - ramp_smooth_floor = eeprom[ramp_smooth_floor_e]; - ramp_smooth_ceil = eeprom[ramp_smooth_ceil_e]; - ramp_discrete_floor = eeprom[ramp_discrete_floor_e]; - ramp_discrete_ceil = eeprom[ramp_discrete_ceil_e]; - ramp_discrete_steps = eeprom[ramp_discrete_steps_e]; - #endif - #ifdef USE_TINT_RAMPING - tint = eeprom[tint_e]; - #endif - #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) - strobe_type = eeprom[strobe_type_e]; // TODO: move this to eeprom_wl? - strobe_delays[0] = eeprom[strobe_delays_0_e]; - strobe_delays[1] = eeprom[strobe_delays_1_e]; - #endif - #ifdef USE_BIKE_FLASHER_MODE - bike_flasher_brightness = eeprom[bike_flasher_brightness_e]; - #endif - #ifdef USE_BEACON_MODE - beacon_seconds = eeprom[beacon_seconds_e]; - #endif - #ifdef USE_MUGGLE_MODE - muggle_mode_active = eeprom[muggle_mode_active_e]; - #endif - #ifdef USE_THERMAL_REGULATION - therm_ceil = eeprom[therm_ceil_e]; - therm_cal_offset = eeprom[therm_cal_offset_e]; - #endif - #ifdef USE_INDICATOR_LED - indicator_led_mode = eeprom[indicator_led_mode_e]; - #endif - } - #ifdef START_AT_MEMORIZED_LEVEL - if (load_eeprom_wl()) { - memorized_level = eeprom_wl[0]; - } - #endif -} - -void save_config() { - eeprom[ramp_style_e] = ramp_style; - #ifdef USE_RAMP_CONFIG - eeprom[ramp_smooth_floor_e] = ramp_smooth_floor; - eeprom[ramp_smooth_ceil_e] = ramp_smooth_ceil; - eeprom[ramp_discrete_floor_e] = ramp_discrete_floor; - eeprom[ramp_discrete_ceil_e] = ramp_discrete_ceil; - eeprom[ramp_discrete_steps_e] = ramp_discrete_steps; - #endif - #ifdef USE_TINT_RAMPING - eeprom[tint_e] = tint; - #endif - #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) - eeprom[strobe_type_e] = strobe_type; // TODO: move this to eeprom_wl? - eeprom[strobe_delays_0_e] = strobe_delays[0]; - eeprom[strobe_delays_1_e] = strobe_delays[1]; - #endif - #ifdef USE_BIKE_FLASHER_MODE - eeprom[bike_flasher_brightness_e] = bike_flasher_brightness; - #endif - #ifdef USE_BEACON_MODE - eeprom[beacon_seconds_e] = beacon_seconds; - #endif - #ifdef USE_MUGGLE_MODE - eeprom[muggle_mode_active_e] = muggle_mode_active; - #endif - #ifdef USE_THERMAL_REGULATION - eeprom[therm_ceil_e] = therm_ceil; - eeprom[therm_cal_offset_e] = therm_cal_offset; - #endif - #ifdef USE_INDICATOR_LED - eeprom[indicator_led_mode_e] = indicator_led_mode; - #endif - - save_eeprom(); -} - -#ifdef START_AT_MEMORIZED_LEVEL -void save_config_wl() { - eeprom_wl[0] = memorized_level; - save_eeprom_wl(); -} -#endif - - -void low_voltage() { - StatePtr state = current_state; - - // TODO: turn off aux LED(s) when power is really low - - if (0) {} // placeholder - - #ifdef USE_STROBE_STATE - // "step down" from strobe to something low - else if (state == strobe_state) { - set_state(steady_state, RAMP_SIZE/6); - } - #endif - - // in normal or muggle mode, step down or turn off - //else if ((state == steady_state) || (state == muggle_state)) { - else if (state == steady_state) { - if (actual_level > 1) { - uint8_t lvl = (actual_level >> 1) + (actual_level >> 2); - set_level(lvl); - #ifdef USE_THERMAL_REGULATION - target_level = lvl; - #ifdef USE_SET_LEVEL_GRADUALLY - // not needed? - //set_level_gradually(lvl); - #endif - #endif - } - else { - set_state(off_state, 0); - } - } - // all other modes, just turn off when voltage is low - else { - set_state(off_state, 0); - } -} - - -void setup() { - #ifdef START_AT_MEMORIZED_LEVEL - // dual switch: e-switch + power clicky - // power clicky acts as a momentary mode - load_config(); - - #ifdef USE_MUGGLE_MODE - if (muggle_mode_active) - push_state(muggle_state, memorized_level); - else - #endif - if (button_is_pressed()) - // hold button to go to moon - push_state(steady_state, 1); - else - // otherwise use memory - push_state(steady_state, memorized_level); - - #else // if not START_AT_MEMORIZED_LEVEL - - // blink at power-on to let user know power is connected - set_level(RAMP_SIZE/8); - delay_4ms(3); - set_level(0); - - load_config(); - - #ifdef USE_TINT_RAMPING - // add tint ramping underneath every other state - push_state(tint_ramping_state, 0); - #endif // ifdef USE_TINT_RAMPING - - #ifdef USE_MUGGLE_MODE - if (muggle_mode_active) - push_state(muggle_state, (MUGGLE_FLOOR+MUGGLE_CEILING)/2); - else - #endif - push_state(off_state, 0); - - #endif // ifdef START_AT_MEMORIZED_LEVEL -} - - -void loop() { - - StatePtr state = current_state; - - if (0) {} - - #ifdef USE_STROBE_STATE - else if ((state == strobe_state) - || ((state == momentary_state) && (momentary_mode == 1) && (momentary_active)) ) { // also handle momentary strobes - uint8_t st = strobe_type; - - switch(st) { - #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) - #ifdef USE_PARTY_STROBE_MODE - case party_strobe_e: - #endif - #ifdef USE_TACTICAL_STROBE_MODE - case tactical_strobe_e: - #endif - party_tactical_strobe_mode_iter(st); - break; - #endif - - #ifdef USE_LIGHTNING_MODE - case lightning_storm_e: - lightning_storm_iter(); - break; - #endif - - #ifdef USE_BIKE_FLASHER_MODE - case bike_flasher_e: - bike_flasher_iter(); - break; - #endif - } - - } - #endif // #ifdef USE_STROBE_STATE - - #ifdef USE_BORING_STROBE_STATE - else if ((state == boring_strobe_state) - || ((state == momentary_state) && (momentary_mode == 1) && (momentary_active)) ) { // also handle momentary strobes - switch(boring_strobe_type) { - #ifdef USE_POLICE_STROBE_MODE - case 0: // police strobe - police_strobe_iter(); - break; - #endif - - #ifdef USE_SOS_MODE - default: // SOS - sos_mode_iter(); - break; - #endif - } - } - #endif // #ifdef USE_BORING_STROBE_STATE - - #ifdef USE_BATTCHECK - else if (state == battcheck_state) { - battcheck(); - } - #endif - - #ifdef USE_BEACON_MODE - else if (state == beacon_state) { - beacon_mode_iter(); - } - #endif - - #ifdef USE_THERMAL_REGULATION - // TODO: blink out therm_ceil during thermal_config_state? - else if (state == tempcheck_state) { - blink_num(temperature); - nice_delay_ms(1000); - } - #endif - - #ifdef USE_IDLE_MODE - else { - // doze until next clock tick - idle_mode(); - } - #endif - -} diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c deleted file mode 100644 index 31b250f..0000000 --- a/spaghetti-monster/fsm-adc.c +++ /dev/null @@ -1,573 +0,0 @@ -// fsm-adc.c: ADC (voltage, temperature) functions for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -// override onboard temperature sensor definition, if relevant -#ifdef USE_EXTERNAL_TEMP_SENSOR -#ifdef ADMUX_THERM -#undef ADMUX_THERM -#endif -#define ADMUX_THERM ADMUX_THERM_EXTERNAL_SENSOR -#endif - -#include - - -static inline void set_admux_therm() { - #if (ATTINY == 1634) - ADMUX = ADMUX_THERM; - #elif (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) - ADMUX = ADMUX_THERM | (1 << ADLAR); - #elif (ATTINY == 841) // FIXME: not tested - ADMUXA = ADMUXA_THERM; - ADMUXB = ADMUXB_THERM; - #elif defined(AVRXMEGA3) // ATTINY816, 817, etc - ADC0.MUXPOS = ADC_MUXPOS_TEMPSENSE_gc; // read temperature - ADC0.CTRLC = ADC_SAMPCAP_bm | ADC_PRESC_DIV64_gc | ADC_REFSEL_INTREF_gc; // Internal ADC reference - #else - #error Unrecognized MCU type - #endif - adc_channel = 1; - adc_sample_count = 0; // first result is unstable - ADC_start_measurement(); -} - -inline void set_admux_voltage() { - #if (ATTINY == 1634) - #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 - ADMUX = ADMUX_VOLTAGE_DIVIDER; - #else // VCC / 1.1V reference - ADMUX = ADMUX_VCC; - #endif - #elif (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) - #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 - ADMUX = ADMUX_VOLTAGE_DIVIDER | (1 << ADLAR); - #else // VCC / 1.1V reference - ADMUX = ADMUX_VCC | (1 << ADLAR); - #endif - #elif (ATTINY == 841) // FIXME: not tested - #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7 - ADMUXA = ADMUXA_VOLTAGE_DIVIDER; - ADMUXB = ADMUXB_VOLTAGE_DIVIDER; - #else // VCC / 1.1V reference - ADMUXA = ADMUXA_VCC; - ADMUXB = ADMUXB_VCC; - #endif - #elif defined(AVRXMEGA3) // ATTINY816, 817, etc - #ifdef USE_VOLTAGE_DIVIDER // 1.1V / ADC input pin - // verify that this is correct!!! untested - ADC0.MUXPOS = ADMUX_VOLTAGE_DIVIDER; // read the requested ADC pin - ADC0.CTRLC = ADC_SAMPCAP_bm | ADC_PRESC_DIV64_gc | ADC_REFSEL_INTREF_gc; // Use internal ADC reference - #else // VCC / 1.1V reference - ADC0.MUXPOS = ADC_MUXPOS_INTREF_gc; // read internal reference - ADC0.CTRLC = ADC_SAMPCAP_bm | ADC_PRESC_DIV64_gc | ADC_REFSEL_VDDREF_gc; // Vdd (Vcc) be ADC reference - #endif - #else - #error Unrecognized MCU type - #endif - adc_channel = 0; - adc_sample_count = 0; // first result is unstable - ADC_start_measurement(); -} - - -#ifdef TICK_DURING_STANDBY -inline void adc_sleep_mode() { - // needs a special sleep mode to get accurate measurements quickly - // ... full power-down ends up using more power overall, and causes - // some weird issues when the MCU doesn't stay awake enough cycles - // to complete a reading - #ifdef SLEEP_MODE_ADC - // attiny1634 - set_sleep_mode(SLEEP_MODE_ADC); - #elif defined(AVRXMEGA3) // ATTINY816, 817, etc - set_sleep_mode(SLEEP_MODE_STANDBY); - #else - #error No ADC sleep mode defined for this hardware. - #endif -} -#endif - -inline void ADC_start_measurement() { - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 841) || (ATTINY == 1634) - ADCSRA |= (1 << ADSC) | (1 << ADIE); - #elif defined(AVRXMEGA3) // ATTINY816, 817, etc - ADC0.INTCTRL |= ADC_RESRDY_bm; // enable interrupt - ADC0.COMMAND |= ADC_STCONV_bm; // Start the ADC conversions - #else - #error unrecognized MCU type - #endif -} - -// set up ADC for reading battery voltage -inline void ADC_on() -{ - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634) - set_admux_voltage(); - #ifdef USE_VOLTAGE_DIVIDER - // disable digital input on divider pin to reduce power consumption - VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); - #else - // disable digital input on VCC pin to reduce power consumption - //VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); // FIXME: unsure how to handle for VCC pin - #endif - #if (ATTINY == 1634) - //ACSRA |= (1 << ACD); // turn off analog comparator to save power - ADCSRB |= (1 << ADLAR); // left-adjust flag is here instead of ADMUX - #endif - // enable, start, auto-retrigger, prescale - ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | ADC_PRSCL; - // end tiny25/45/85 - #elif (ATTINY == 841) // FIXME: not tested, missing left-adjust - ADCSRB = 0; // Right adjusted, auto trigger bits cleared. - //ADCSRA = (1 << ADEN ) | 0b011; // ADC on, prescaler division factor 8. - set_admux_voltage(); - // enable, start, auto-retrigger, prescale - ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | ADC_PRSCL; - //ADCSRA |= (1 << ADSC); // start measuring - #elif defined(AVRXMEGA3) // ATTINY816, 817, etc - VREF.CTRLA |= VREF_ADC0REFSEL_1V1_gc; // Set Vbg ref to 1.1V - // Enabled, free-running (aka, auto-retrigger), run in standby - ADC0.CTRLA = ADC_ENABLE_bm | ADC_FREERUN_bm | ADC_RUNSTBY_bm; - // set a INITDLY value because the AVR manual says so (section 30.3.5) - // (delay 1st reading until Vref is stable) - ADC0.CTRLD |= ADC_INITDLY_DLY16_gc; - set_admux_voltage(); - #else - #error Unrecognized MCU type - #endif -} - -inline void ADC_off() { - #ifdef AVRXMEGA3 // ATTINY816, 817, etc - ADC0.CTRLA &= ~(ADC_ENABLE_bm); // disable the ADC - #else - ADCSRA &= ~(1<> 1; - return result; -} -#endif - -// Each full cycle runs ~2X per second with just voltage enabled, -// or ~1X per second with voltage and temperature. -#if defined(USE_LVP) && defined(USE_THERMAL_REGULATION) -#define ADC_CYCLES_PER_SECOND 1 -#else -#define ADC_CYCLES_PER_SECOND 2 -#endif - -#ifdef AVRXMEGA3 // ATTINY816, 817, etc -#define ADC_vect ADC0_RESRDY_vect -#endif -// happens every time the ADC sampler finishes a measurement -ISR(ADC_vect) { - - #ifdef AVRXMEGA3 // ATTINY816, 817, etc - ADC0.INTFLAGS = ADC_RESRDY_bm; // clear the interrupt - #endif - - if (adc_sample_count) { - - uint16_t m; // latest measurement - uint16_t s; // smoothed measurement - uint8_t channel = adc_channel; - - // update the latest value - #ifdef AVRXMEGA3 // ATTINY816, 817, etc - // Use the factory calibrated values in SIGROW.TEMPSENSE0 and SIGROW.TEMPSENSE1 - // to calculate a temperature reading in Kelvin, then left-align it. - if (channel == 1) { // thermal, convert ADC reading to left-aligned Kelvin - int8_t sigrow_offset = SIGROW.TEMPSENSE1; // Read signed value from signature row - uint8_t sigrow_gain = SIGROW.TEMPSENSE0; // Read unsigned value from signature row - uint32_t temp = ADC0.RES - sigrow_offset; - temp *= sigrow_gain; // Result might overflow 16 bit variable (10bit+8bit) - temp += 0x80; // Add 1/2 to get correct rounding on division below - temp >>= 8; // Divide result to get Kelvin - m = (temp << 6); // left align it - } - else { m = (ADC0.RES << 6); } // voltage, force left-alignment - - #else - m = ADC; - #endif - adc_raw[channel] = m; - - // lowpass the value - //s = adc_smooth[channel]; // easier to read - uint16_t *v = adc_smooth + channel; // compiles smaller - s = *v; - if (m > s) { s++; } - if (m < s) { s--; } - //adc_smooth[channel] = s; - *v = s; - - // track what woke us up, and enable deferred logic - irq_adc = 1; - - } - - // the next measurement isn't the first - adc_sample_count = 1; - // rollover doesn't really matter - //adc_sample_count ++; - -} - -void adc_deferred() { - irq_adc = 0; // event handled - - #ifdef USE_PSEUDO_RAND - // real-world entropy makes this a true random, not pseudo - // Why here instead of the ISR? Because it makes the time-critical ISR - // code a few cycles faster and we don't need crypto-grade randomness. - #ifdef AVRXMEGA3 // ATTINY816, 817, etc - pseudo_rand_seed += ADC0.RESL; // right aligned, not left... so should be equivalent? - #else - pseudo_rand_seed += (ADCL >> 6) + (ADCH << 2); - #endif - #endif - - // the ADC triggers repeatedly when it's on, but we only need to run the - // voltage and temperature regulation stuff once in a while...so disable - // this after each activation, until it's manually enabled again - if (! adc_deferred_enable) return; - - // disable after one iteration - adc_deferred_enable = 0; - - // what is being measured? 0 = battery voltage, 1 = temperature - uint8_t adc_step; - - #if defined(USE_LVP) && defined(USE_THERMAL_REGULATION) - // do whichever one is currently active - adc_step = adc_channel; - #else - // unless there's no temperature sensor... then just do voltage - adc_step = 0; - #endif - - #if defined(TICK_DURING_STANDBY) && defined(USE_SLEEP_LVP) - // in sleep mode, turn off after just one measurement - // (having the ADC on raises standby power by about 250 uA) - // (and the usual standby level is only ~20 uA) - if (go_to_standby) { - ADC_off(); - // if any measurements were in progress, they're done now - adc_active_now = 0; - // also, only check the battery while asleep, not the temperature - adc_channel = 0; - } - #endif - - if (0) {} // placeholder for easier syntax - - #ifdef USE_LVP - else if (0 == adc_step) { // voltage - ADC_voltage_handler(); - #ifdef USE_THERMAL_REGULATION - // set the correct type of measurement for next time - if (! go_to_standby) set_admux_therm(); - #endif - } - #endif - - #ifdef USE_THERMAL_REGULATION - else if (1 == adc_step) { // temperature - ADC_temperature_handler(); - #ifdef USE_LVP - // set the correct type of measurement for next time - set_admux_voltage(); - #endif - } - #endif - - if (adc_reset) adc_reset --; -} - - -#ifdef USE_LVP -static inline void ADC_voltage_handler() { - // rate-limit low-voltage warnings to a max of 1 per N seconds - static uint8_t lvp_timer = 0; - #define LVP_TIMER_START (VOLTAGE_WARNING_SECONDS*ADC_CYCLES_PER_SECOND) // N seconds between LVP warnings - - #ifdef NO_LVP_WHILE_BUTTON_PRESSED - // don't run if button is currently being held - // (because the button causes a reading of zero volts) - if (button_last_state) return; - #endif - - uint16_t measurement; - - // latest ADC value - if (adc_reset) { // just after waking, don't lowpass - measurement = adc_raw[0]; - adc_smooth[0] = measurement; // no lowpass, just use the latest value - } - #ifdef USE_LOWPASS_WHILE_ASLEEP - else if (go_to_standby) { // weaker lowpass while asleep - // occasionally the aux LED color can oscillate during standby, - // while using "voltage" mode ... so try to reduce the oscillation - uint16_t r = adc_raw[0]; - uint16_t s = adc_smooth[0]; - #if 0 - // fixed-rate lowpass, stable but very slow - // (move by only 0.5 ADC units per measurement, 1 ADC unit = 64) - if (r < s) { s -= 32; } - if (r > s) { s += 32; } - #elif 1 - // 1/8th proportional lowpass, faster but less stable - int16_t diff = (r/8) - (s/8); - s += diff; - #else - // 50% proportional lowpass, fastest but least stable - s = (r>>1) + (s>>1); - #endif - adc_smooth[0] = s; - measurement = s; - } - #endif - else measurement = adc_smooth[0]; - - // values stair-step between intervals of 64, with random variations - // of 1 or 2 in either direction, so if we chop off the last 6 bits - // it'll flap between N and N-1... but if we add half an interval, - // the values should be really stable after right-alignment - // (instead of 99.98, 100.00, and 100.02, it'll hit values like - // 100.48, 100.50, and 100.52... which are stable when truncated) - //measurement += 32; - //measurement = (measurement + 16) >> 5; - measurement = (measurement + 16) & 0xffe0; // 1111 1111 1110 0000 - - #ifdef USE_VOLTAGE_DIVIDER - voltage = calc_voltage_divider(measurement); - #else - // calculate actual voltage: volts * 10 - // ADC = 1.1 * 1024 / volts - // volts = 1.1 * 1024 / ADC - voltage = ((uint16_t)(2*1.1*1024*10)/(measurement>>6) - + VOLTAGE_FUDGE_FACTOR - #ifdef USE_VOLTAGE_CORRECTION - + VOLT_CORR - 7 - #endif - ) >> 1; - #endif - - // if low, callback EV_voltage_low / EV_voltage_critical - // (but only if it has been more than N seconds since last call) - if (lvp_timer) { - lvp_timer --; - } else { // it has been long enough since the last warning - #ifdef DUAL_VOLTAGE_FLOOR - if (((voltage < VOLTAGE_LOW) && (voltage > DUAL_VOLTAGE_FLOOR)) || (voltage < DUAL_VOLTAGE_LOW_LOW)) { - #else - if (voltage < VOLTAGE_LOW) { - #endif - // send out a warning - emit(EV_voltage_low, 0); - // reset rate-limit counter - lvp_timer = LVP_TIMER_START; - } - } -} -#endif - - -#ifdef USE_THERMAL_REGULATION -// generally happens once per second while awake -static inline void ADC_temperature_handler() { - // coarse adjustment - #ifndef THERM_LOOKAHEAD - #define THERM_LOOKAHEAD 4 - #endif - // reduce frequency of minor warnings - #ifndef THERM_NEXT_WARNING_THRESHOLD - #define THERM_NEXT_WARNING_THRESHOLD 24 - #endif - // fine-grained adjustment - // how proportional should the adjustments be? - #ifndef THERM_RESPONSE_MAGNITUDE - #define THERM_RESPONSE_MAGNITUDE 64 - #endif - // acceptable temperature window size in C - #define THERM_WINDOW_SIZE 2 - - // TODO? make this configurable per build target? - // (shorter time for hosts with a lower power-to-mass ratio) - // (because then it'll have smaller responses) - #define NUM_TEMP_HISTORY_STEPS 8 // don't change; it'll break stuff - static uint8_t history_step = 0; - static uint16_t temperature_history[NUM_TEMP_HISTORY_STEPS]; - static int8_t warning_threshold = 0; - - if (adc_reset) { // wipe out old data - // ignore average, use latest sample - uint16_t foo = adc_raw[1]; - adc_smooth[1] = foo; - - // forget any past measurements - for(uint8_t i=0; i> 5; - } - - // latest 16-bit ADC reading - uint16_t measurement = adc_smooth[1]; - - // values stair-step between intervals of 64, with random variations - // of 1 or 2 in either direction, so if we chop off the last 6 bits - // it'll flap between N and N-1... but if we add half an interval, - // the values should be really stable after right-alignment - // (instead of 99.98, 100.00, and 100.02, it'll hit values like - // 100.48, 100.50, and 100.52... which are stable when truncated) - //measurement += 32; - measurement = (measurement + 16) >> 5; - //measurement = (measurement + 16) & 0xffe0; // 1111 1111 1110 0000 - - // let the UI see the current temperature in C - // Convert ADC units to Celsius (ish) - #ifndef USE_EXTERNAL_TEMP_SENSOR - // onboard sensor for attiny25/45/85/1634 - temperature = (measurement>>1) + THERM_CAL_OFFSET + (int16_t)TH_CAL - 275; - #else - // external sensor - temperature = EXTERN_TEMP_FORMULA(measurement>>1) + THERM_CAL_OFFSET + (int16_t)TH_CAL; - #endif - - // how much has the temperature changed between now and a few seconds ago? - int16_t diff; - diff = measurement - temperature_history[history_step]; - - // update / rotate the temperature history - temperature_history[history_step] = measurement; - history_step = (history_step + 1) & (NUM_TEMP_HISTORY_STEPS-1); - - // PI[D]: guess what the temperature will be in a few seconds - uint16_t pt; // predicted temperature - pt = measurement + (diff * THERM_LOOKAHEAD); - - // convert temperature limit from C to raw 16-bit ADC units - // C = (ADC>>6) - 275 + THERM_CAL_OFFSET + TH_CAL; - // ... so ... - // (C + 275 - THERM_CAL_OFFSET - TH_CAL) << 6 = ADC; - uint16_t ceil = (TH_CEIL + 275 - TH_CAL - THERM_CAL_OFFSET) << 1; - int16_t offset = pt - ceil; - - // bias small errors toward zero, while leaving large errors mostly unaffected - // (a diff of 1 C is 2 ADC units, * 4 for therm lookahead, so it becomes 8) - // (but a diff of 1 C should only send a warning of magnitude 1) - // (this also makes it only respond to small errors at the time the error - // happened, not after the temperature has stabilized) - for(uint8_t foo=0; foo<3; foo++) { - if (offset > 0) { - offset --; - } else if (offset < 0) { - offset ++; - } - } - - // Too hot? - // (if it's too hot and not getting cooler...) - if ((offset > 0) && (diff > -1)) { - // accumulated error isn't big enough yet to send a warning - if (warning_threshold > 0) { - warning_threshold -= offset; - } else { // error is big enough; send a warning - // how far above the ceiling? - // original method works, but is too slow on some small hosts: - // (and typically has a minimum response magnitude of 2 instead of 1) - // int16_t howmuch = offset; - // ... so increase the amount, except for small values - // (for example, 1:1, 2:1, 3:3, 4:5, 6:9, 8:13, 10:17, 40:77) - // ... and let us tune the response per build target if desired - int16_t howmuch = (offset + offset - 3) * THERM_RESPONSE_MAGNITUDE / 128; - if (howmuch < 1) howmuch = 1; - warning_threshold = THERM_NEXT_WARNING_THRESHOLD - (uint8_t)howmuch; - - // send a warning - emit(EV_temperature_high, howmuch); - } - } - - // Too cold? - // (if it's too cold and still getting colder...) - // the temperature is this far below the floor: - #define BELOW (offset + (THERM_WINDOW_SIZE<<1)) - else if ((BELOW < 0) && (diff < 0)) { - // accumulated error isn't big enough yet to send a warning - if (warning_threshold < 0) { - warning_threshold -= BELOW; - } else { // error is big enough; send a warning - warning_threshold = (-THERM_NEXT_WARNING_THRESHOLD) - BELOW; - - // how far below the floor? - // int16_t howmuch = ((-BELOW) >> 1) * THERM_RESPONSE_MAGNITUDE / 128; - int16_t howmuch = (-BELOW) >> 1; - // send a notification (unless voltage is low) - // (LVP and underheat warnings fight each other) - if (voltage > (VOLTAGE_LOW + 1)) - emit(EV_temperature_low, howmuch); - } - } - #undef BELOW - - // Goldilocks? - // (temperature is within target window, or at least heading toward it) - else { - // send a notification (unless voltage is low) - // (LVP and temp-okay events fight each other) - if (voltage > VOLTAGE_LOW) - emit(EV_temperature_okay, 0); - } -} -#endif - - -#ifdef USE_BATTCHECK -#ifdef BATTCHECK_4bars -PROGMEM const uint8_t voltage_blinks[] = { - 30, 35, 38, 40, 42, 99, -}; -#endif -#ifdef BATTCHECK_6bars -PROGMEM const uint8_t voltage_blinks[] = { - 30, 34, 36, 38, 40, 41, 43, 99, -}; -#endif -#ifdef BATTCHECK_8bars -PROGMEM const uint8_t voltage_blinks[] = { - 30, 33, 35, 37, 38, 39, 40, 41, 42, 99, -}; -#endif -void battcheck() { - #ifdef BATTCHECK_VpT - blink_num(voltage); - #else - uint8_t i; - for(i=0; - voltage >= pgm_read_byte(voltage_blinks + i); - i++) {} - #ifdef DONT_DELAY_AFTER_BATTCHECK - blink_digit(i); - #else - if (blink_digit(i)) - nice_delay_ms(1000); - #endif - #endif -} -#endif - diff --git a/spaghetti-monster/fsm-adc.h b/spaghetti-monster/fsm-adc.h deleted file mode 100644 index 1bb67ed..0000000 --- a/spaghetti-monster/fsm-adc.h +++ /dev/null @@ -1,112 +0,0 @@ -// fsm-adc.h: ADC (voltage, temperature) functions for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#if defined(USE_LVP) || defined(USE_THERMAL_REGULATION) -// use raw value instead of lowpassed value for the next N measurements -// (2 = 1 for voltage + 1 for temperature) -volatile uint8_t adc_reset = 2; -#endif - -#ifdef USE_LVP -// default 5 seconds between low-voltage warning events -#ifndef VOLTAGE_WARNING_SECONDS -#define VOLTAGE_WARNING_SECONDS 5 -#endif -// low-battery threshold in volts * 10 -#ifndef VOLTAGE_LOW -#define VOLTAGE_LOW 29 -#endif -// battery is low but not critical -#ifndef VOLTAGE_RED -#define VOLTAGE_RED 33 -#endif -// MCU sees voltage 0.X volts lower than actual, add X/2 to readings -#ifndef VOLTAGE_FUDGE_FACTOR -#ifdef USE_VOLTAGE_DIVIDER -#define VOLTAGE_FUDGE_FACTOR 0 -#else -#define VOLTAGE_FUDGE_FACTOR 5 -#endif -#endif - -#ifdef TICK_DURING_STANDBY -volatile uint8_t adc_active_now = 0; // sleep LVP needs a different sleep mode -#endif -volatile uint8_t irq_adc = 0; // ADC interrupt happened? -uint8_t adc_sample_count = 0; // skip the first sample; it's junk -uint8_t adc_channel = 0; // 0=voltage, 1=temperature -uint16_t adc_raw[2]; // last ADC measurements (0=voltage, 1=temperature) -uint16_t adc_smooth[2]; // lowpassed ADC measurements (0=voltage, 1=temperature) -// ADC code is split into two parts: -// - ISR: runs immediately at each interrupt, does the bare minimum because time is critical here -// - deferred: the bulk of the logic runs later when time isn't so critical -uint8_t adc_deferred_enable = 0; // stop waiting and run the deferred code -void adc_deferred(); // do the actual ADC-related calculations - -static inline void ADC_voltage_handler(); -uint8_t voltage = 0; -#ifdef USE_VOLTAGE_CORRECTION - #ifdef USE_CFG - #define VOLT_CORR cfg.voltage_correction - #else - // same 0.05V units as fudge factor, - // but 7 is neutral, and the expected range is from 1 to 13 - uint8_t voltage_correction = 7; - #define VOLT_CORR voltage_correction - #endif -#endif -#ifdef USE_LVP -void low_voltage(); -#endif - -#ifdef USE_BATTCHECK -void battcheck(); -#ifdef BATTCHECK_VpT -#define USE_BLINK_NUM -#endif -#if defined(BATTCHECK_8bars) || defined(BATTCHECK_6bars) || defined(BATTCHECK_4bars) -#define USE_BLINK_DIGIT -#endif -#endif -#endif // ifdef USE_LVP - - -#ifdef USE_THERMAL_REGULATION -// try to keep temperature below 45 C -#ifndef DEFAULT_THERM_CEIL -#define DEFAULT_THERM_CEIL 45 -#endif -// don't allow user to set ceiling above 70 C -#ifndef MAX_THERM_CEIL -#define MAX_THERM_CEIL 70 -#endif -// Local per-MCU calibration value -#ifndef THERM_CAL_OFFSET -#define THERM_CAL_OFFSET 0 -#endif -// temperature now, in C (ish) -int16_t temperature; -#ifdef USE_CFG - #define TH_CEIL cfg.therm_ceil - #define TH_CAL cfg.therm_cal_offset -#else - #define TH_CEIL therm_ceil - #define TH_CAL therm_cal_offset - uint8_t therm_ceil = DEFAULT_THERM_CEIL; - int8_t therm_cal_offset = 0; -#endif -static inline void ADC_temperature_handler(); -#endif // ifdef USE_THERMAL_REGULATION - - -inline void ADC_on(); -inline void ADC_off(); -inline void ADC_start_measurement(); - -#ifdef TICK_DURING_STANDBY -inline void adc_sleep_mode(); -#endif - diff --git a/spaghetti-monster/fsm-channels.c b/spaghetti-monster/fsm-channels.c deleted file mode 100644 index cc78536..0000000 --- a/spaghetti-monster/fsm-channels.c +++ /dev/null @@ -1,357 +0,0 @@ -// fsm-channels.c: Channel mode functions for SpaghettiMonster. -// Copyright (C) 2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "fsm-ramping.h" - - -#if NUM_CHANNEL_MODES > 1 -void set_channel_mode(uint8_t mode) { - if (mode == channel_mode) return; // abort if nothing to do - - uint8_t cur_level = actual_level; - - // turn off old LEDs before changing channel - set_level(0); - - // change the channel - channel_mode = mode; - - // update the LEDs - set_level(cur_level); -} -#endif // if NUM_CHANNEL_MODES > 1 - - -#ifdef USE_CALC_2CH_BLEND -// calculate a "tint ramp" blend between 2 channels -// results are placed in *warm and *cool vars -// brightness : total amount of light units to distribute -// top : maximum allowed brightness per channel -// blend : ratio between warm and cool (0 = warm, 128 = 50%, 255 = cool) -void calc_2ch_blend( - PWM_DATATYPE *warm, - PWM_DATATYPE *cool, - PWM_DATATYPE brightness, - PWM_DATATYPE top, - uint8_t blend) { - - #ifndef TINT_RAMPING_CORRECTION - #define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint - #endif - - // calculate actual PWM levels based on a single-channel ramp - // and a blend value - PWM_DATATYPE warm_PWM, cool_PWM; - PWM_DATATYPE2 base_PWM = brightness; - - #if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0) - uint8_t level = actual_level - 1; - - // middle tints sag, so correct for that effect - // by adding extra power which peaks at the middle tint - // (correction is only necessary when PWM is fast) - if (level > HALFSPEED_LEVEL) { - base_PWM = brightness - + ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64) - * triangle_wave(blend) / 255); - } - // fade the triangle wave out when above 100% power, - // so it won't go over 200% - if (brightness > top) { - base_PWM -= 2 * ( - ((brightness - top) * TINT_RAMPING_CORRECTION / 64) - * triangle_wave(blend) / 255 - ); - } - // guarantee no more than 200% power - if (base_PWM > (top << 1)) { base_PWM = top << 1; } - #endif - - cool_PWM = (((PWM_DATATYPE2)blend * (PWM_DATATYPE2)base_PWM) + 127) / 255; - warm_PWM = base_PWM - cool_PWM; - // when running at > 100% power, spill extra over to other channel - if (cool_PWM > top) { - warm_PWM += (cool_PWM - top); - cool_PWM = top; - } else if (warm_PWM > top) { - cool_PWM += (warm_PWM - top); - warm_PWM = top; - } - - *warm = warm_PWM; - *cool = cool_PWM; -} -#endif // ifdef USE_CALC_2CH_BLEND - - -#ifdef USE_HSV2RGB -RGB_t hsv2rgb(uint8_t h, uint8_t s, uint16_t v) { - RGB_t color; - - if (s == 0) { // grey - color.r = color.g = color.b = v; - return color; - } - - uint8_t region; - uint16_t fpart; - uint16_t high, low, rising, falling; - - // hue has 6 segments, 0-5 - region = ((uint16_t)h * 6) >> 8; - // find remainder part, make it from 0-255 - fpart = ((uint16_t)h * 6) - (region << 8); - - // calculate graph segments, doing integer multiplication - // TODO: calculate 16-bit results, not 8-bit - high = v; - low = ((uint32_t)v * (255 - s)) >> 8; - // TODO: use a cosine crossfade instead of linear - // (because it looks better and feels more natural) - falling = ((uint32_t)v * (255 - ((s * fpart) >> 8))) >> 8; - rising = ((uint32_t)v * (255 - ((s * (255 - fpart)) >> 8))) >> 8; - - // default floor - color.r = low; - color.g = low; - color.b = low; - - // assign graph shapes based on color cone region - switch (region) { - case 0: - color.r = high; - color.g = rising; - //color.b = low; - break; - case 1: - color.r = falling; - color.g = high; - //color.b = low; - break; - case 2: - //color.r = low; - color.g = high; - color.b = rising; - break; - case 3: - //color.r = low; - color.g = falling; - color.b = high; - break; - case 4: - color.r = rising; - //color.g = low; - color.b = high; - break; - default: - color.r = high; - //color.g = low; - color.b = falling; - break; - } - - return color; -} -#endif // ifdef USE_HSV2RGB - - -///// Common set_level_*() functions shared by multiple lights ///// -// (unique lights should use their own, -// but these common versions cover most of the common hardware designs) - -// TODO: upgrade some older lights to dynamic PWM -// TODO: 1ch w/ dynamic PWM -// TODO: 1ch w/ dynamic PWM and opamp enable pins? -// TODO: 2ch stacked w/ dynamic PWM -// TODO: 2ch stacked w/ dynamic PWM and opamp enable pins? - - -#ifdef USE_SET_LEVEL_1CH -// single set of LEDs with 1 power channel -void set_level_1ch(uint8_t level) { - if (level == 0) { - LOW_PWM_LVL = 0; - } else { - level --; // PWM array index = level - 1 - LOW_PWM_LVL = PWM_GET(low_pwm_levels, level); - } -} -#endif - - -#ifdef USE_SET_LEVEL_2CH_STACKED -// single set of LEDs with 2 stacked power channels, DDFET+1 or DDFET+linear -void set_level_2ch_stacked(uint8_t level) { - if (level == 0) { - LOW_PWM_LVL = 0; - HIGH_PWM_LVL = 0; - } else { - level --; // PWM array index = level - 1 - LOW_PWM_LVL = PWM_GET(low_pwm_levels, level); - HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level); - } -} -#endif - - -#ifdef USE_SET_LEVEL_3CH_STACKED -// single set of LEDs with 3 stacked power channels, like DDFET+N+1 -void set_level_3ch_stacked(uint8_t level) { - if (level == 0) { - LOW_PWM_LVL = 0; - MED_PWM_LVL = 0; - HIGH_PWM_LVL = 0; - } else { - level --; // PWM array index = level - 1 - LOW_PWM_LVL = PWM_GET(low_pwm_levels, level); - MED_PWM_LVL = PWM_GET(med_pwm_levels, level); - HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level); - } -} -#endif - - -#if defined(USE_TINT_RAMPING) && (!defined(TINT_RAMP_TOGGLE_ONLY)) -void set_level_2ch_blend() { - #ifndef TINT_RAMPING_CORRECTION - #define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint - #endif - - // calculate actual PWM levels based on a single-channel ramp - // and a global tint value - //PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level); - uint16_t brightness = PWM1_LVL; - uint16_t warm_PWM, cool_PWM; - #ifdef USE_STACKED_DYN_PWM - uint16_t top = PWM1_TOP; - //uint16_t top = PWM_GET(pwm_tops, actual_level-1); - #else - const uint16_t top = PWM_TOP; - #endif - - // auto-tint modes - uint8_t mytint; - uint8_t level = actual_level - 1; - #if 1 - // perceptual by ramp level - if (tint == 0) { mytint = 255 * (uint16_t)level / RAMP_SIZE; } - else if (tint == 255) { mytint = 255 - (255 * (uint16_t)level / RAMP_SIZE); } - #else - // linear with power level - //if (tint == 0) { mytint = brightness; } - //else if (tint == 255) { mytint = 255 - brightness; } - #endif - // stretch 1-254 to fit 0-255 range (hits every value except 98 and 198) - else { mytint = (tint * 100 / 99) - 1; } - - PWM_DATATYPE2 base_PWM = brightness; - #if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0) - // middle tints sag, so correct for that effect - // by adding extra power which peaks at the middle tint - // (correction is only necessary when PWM is fast) - if (level > HALFSPEED_LEVEL) { - base_PWM = brightness - + ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64) * triangle_wave(mytint) / 255); - } - // fade the triangle wave out when above 100% power, - // so it won't go over 200% - if (brightness > top) { - base_PWM -= 2 * ( - ((brightness - top) * TINT_RAMPING_CORRECTION / 64) - * triangle_wave(mytint) / 255 - ); - } - // guarantee no more than 200% power - if (base_PWM > (top << 1)) { base_PWM = top << 1; } - #endif - - cool_PWM = (((PWM_DATATYPE2)mytint * (PWM_DATATYPE2)base_PWM) + 127) / 255; - warm_PWM = base_PWM - cool_PWM; - // when running at > 100% power, spill extra over to other channel - if (cool_PWM > top) { - warm_PWM += (cool_PWM - top); - cool_PWM = top; - } else if (warm_PWM > top) { - cool_PWM += (warm_PWM - top); - warm_PWM = top; - } - - TINT1_LVL = warm_PWM; - TINT2_LVL = cool_PWM; - - // disable the power channel, if relevant - #ifdef LED_ENABLE_PIN - if (warm_PWM) - LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN); - else - LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN); - #endif - #ifdef LED2_ENABLE_PIN - if (cool_PWM) - LED2_ENABLE_PORT |= (1 << LED2_ENABLE_PIN); - else - LED2_ENABLE_PORT &= ~(1 << LED2_ENABLE_PIN); - #endif -} -#endif // ifdef USE_TINT_RAMPING - - -#ifdef USE_GRADUAL_TICK_1CH -void gradual_tick_1ch() { - GRADUAL_TICK_SETUP(); - - GRADUAL_ADJUST_1CH(low_pwm_levels, LOW_PWM_LVL); - - // did we go far enough to hit the next defined ramp level? - // if so, update the main ramp level tracking var - if ((LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt))) - { - GRADUAL_IS_ACTUAL(); - } -} -#endif - - -#ifdef USE_GRADUAL_TICK_2CH_STACKED -void gradual_tick_2ch_stacked() { - GRADUAL_TICK_SETUP(); - - GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP); - GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL); - - // did we go far enough to hit the next defined ramp level? - // if so, update the main ramp level tracking var - if ( (LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt)) - && (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt)) - ) - { - GRADUAL_IS_ACTUAL(); - } -} -#endif - - -#ifdef USE_GRADUAL_TICK_3CH_STACKED -void gradual_tick_3ch_stacked() { - GRADUAL_TICK_SETUP(); - - GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP); - GRADUAL_ADJUST(med_pwm_levels, MED_PWM_LVL, PWM_TOP); - GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL); - - // did we go far enough to hit the next defined ramp level? - // if so, update the main ramp level tracking var - if ( (LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt)) - && (MED_PWM_LVL == PWM_GET(med_pwm_levels, gt)) - && (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt)) - ) - { - GRADUAL_IS_ACTUAL(); - } -} -#endif - - diff --git a/spaghetti-monster/fsm-channels.h b/spaghetti-monster/fsm-channels.h deleted file mode 100644 index 218f4f5..0000000 --- a/spaghetti-monster/fsm-channels.h +++ /dev/null @@ -1,141 +0,0 @@ -// fsm-channels.h: Channel mode functions for SpaghettiMonster. -// Copyright (C) 2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -// always enable channel modes, even if there is only one -#define USE_CHANNEL_MODES - -// typedefs -typedef void SetLevelFunc(uint8_t level); -typedef SetLevelFunc * SetLevelFuncPtr; - -typedef bool GradualTickFunc(uint8_t gt); -typedef GradualTickFunc * GradualTickFuncPtr; - -// TODO: implement custom 3H handlers -typedef void ChannelArgFunc(); -typedef ChannelArgFunc * ChannelArgFuncPtr; - -typedef struct Channel { - SetLevelFuncPtr set_level; - #ifdef USE_SET_LEVEL_GRADUALLY - GradualTickFuncPtr gradual_tick; - #endif - #ifdef USE_CUSTOM_3H_HANDLERS - // TODO: implement custom 3H handlers - ChannelArgFuncPtr ramp_channel_arg; - #endif - #ifdef USE_CHANNEL_MODE_ARGS - bool has_args; - //uint8_t arg; // is in the config struct, not here - #endif -} Channel; - -Channel channels[]; // values are defined in the hwdef-*.c - -// TODO: size-optimize the case with only 1 channel mode? -// (the arrays and stuff shouldn't be needed) - -#if NUM_CHANNEL_MODES > 1 - #define USE_CHANNEL_MODES - // current multi-channel mode - uint8_t channel_mode = DEFAULT_CHANNEL_MODE; -#else - #define channel_mode 0 -#endif - -#ifdef USE_CUSTOM_CHANNEL_3H_MODES -// different 3H behavior per channel? -// TODO: move to progmem -// TODO: move to Anduril, not FSM -StatePtr channel_3H_modes[NUM_CHANNEL_MODES]; -#endif - -//#ifdef USE_CHANNEL_MODE_TOGGLES -#if NUM_CHANNEL_MODES > 1 -// user can take unwanted modes out of the rotation -// bitmask -#ifdef USE_CFG - #define channel_mode_enabled(n) ((cfg.channel_modes_enabled >> n) & 1) - #define channel_mode_enable(n) cfg.channel_modes_enabled |= (1 << n) - #define channel_mode_disable(n) cfg.channel_modes_enabled &= ((1 << n) ^ 0xff) -#else - uint16_t channel_modes_enabled = CHANNEL_MODES_ENABLED; - #define channel_mode_enabled(n) ((channel_modes_enabled >> n) & 1) - #define channel_mode_enable(n) channel_modes_enabled |= (1 << n) - #define channel_mode_disable(n) channel_modes_enabled &= ((1 << n) ^ 0xff) - #endif -#endif - -#ifdef USE_CHANNEL_MODE_ARGS - #ifndef USE_CFG - // one byte of extra data per channel mode, like for tint value - uint8_t channel_mode_args[NUM_CHANNEL_MODES] = { CHANNEL_MODE_ARGS }; - #endif - // which modes respond to their "arg", and which don't? - //const uint8_t channel_has_args = CHANNEL_HAS_ARGS; - //#define channel_has_args(n) ((CHANNEL_HAS_ARGS >> n) & 1) - // struct member - #define channel_has_args(n) (channels[n].has_args) -#endif - -#if NUM_CHANNEL_MODES > 1 -void set_channel_mode(uint8_t mode); -#endif - -#ifdef USE_CALC_2CH_BLEND -void calc_2ch_blend( - PWM_DATATYPE *warm, - PWM_DATATYPE *cool, - PWM_DATATYPE brightness, - PWM_DATATYPE top, - uint8_t blend); -#endif - -#ifdef USE_HSV2RGB -typedef struct RGB_t { - uint16_t r; - uint16_t g; - uint16_t b; -} RGB_t; -RGB_t hsv2rgb(uint8_t h, uint8_t s, uint16_t v); -#endif // ifdef USE_HSV2RGB - - -#ifdef USE_SET_LEVEL_1CH -// TODO: remove this -void set_level_1ch(uint8_t level); -#endif - -#ifdef USE_SET_LEVEL_2CH_STACKED -// TODO: remove this -void set_level_2ch_stacked(uint8_t level); -#endif - -#ifdef USE_SET_LEVEL_3CH_STACKED -// TODO: remove this -void set_level_3ch_stacked(uint8_t level); -#endif - -#if defined(USE_TINT_RAMPING) && (!defined(TINT_RAMP_TOGGLE_ONLY)) -// TODO: remove this -void set_level_2ch_blend(); -#endif - -#ifdef USE_GRADUAL_TICK_1CH -// TODO: remove this -void gradual_tick_1ch(); -#endif - -#ifdef USE_GRADUAL_TICK_2CH_STACKED -// TODO: remove this -void gradual_tick_2ch_stacked(); -#endif - -#ifdef USE_GRADUAL_TICK_3CH_STACKED -// TODO: remove this -void gradual_tick_3ch_stacked(); -#endif - diff --git a/spaghetti-monster/fsm-eeprom.c b/spaghetti-monster/fsm-eeprom.c deleted file mode 100644 index 66cdd78..0000000 --- a/spaghetti-monster/fsm-eeprom.c +++ /dev/null @@ -1,112 +0,0 @@ -// fsm-eeprom.c: EEPROM API for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "fsm-eeprom.h" - -#ifdef USE_EEPROM -#ifdef EEPROM_OVERRIDE -uint8_t *eeprom; -#else -uint8_t eeprom[EEPROM_BYTES]; -#endif - -uint8_t load_eeprom() { - #if defined(LED_ENABLE_PIN) || defined(LED2_ENABLE_PIN) - delay_4ms(2); // wait for power to stabilize - #endif - - cli(); - // check if eeprom has been initialized; abort if it hasn't - uint8_t marker = eeprom_read_byte((uint8_t *)EEP_START); - if (marker != EEP_MARKER) { sei(); return 0; } - - // load the actual data - for(uint8_t i=0; i (uint8_t *)(EEP_WL_SIZE-EEPROM_WL_BYTES-1)) offset = 0; - eep_wl_prev_offset = offset; - // marker byte - // FIXME: write the marker last, to signal completed transaction - eeprom_update_byte(offset, EEP_MARKER); - offset ++; - // user data - for(uint8_t i=0; i - -// set this higher to enable normal eeprom functions -#ifndef EEPROM_BYTES -#define EEPROM_BYTES 0 -#endif - -// set this higher to enable wear-levelled eeprom functions -#ifndef EEPROM_WL_BYTES -#define EEPROM_WL_BYTES 0 -#endif - -#ifdef USE_EEPROM -// this fails when EEPROM_BYTES is a sizeof() -//#if EEPROM_BYTES >= (EEPSIZE/2) -//#error Requested EEPROM_BYTES too big. -//#endif -#ifdef EEPROM_OVERRIDE -uint8_t *eeprom; -#else -uint8_t eeprom[EEPROM_BYTES]; -#endif -uint8_t load_eeprom(); // returns 1 for success, 0 for no data found -void save_eeprom(); -#define EEP_START (EEPSIZE/2) -#endif - -#ifdef USE_EEPROM_WL -#if EEPROM_WL_BYTES >= (EEPSIZE/4) -#error Requested EEPROM_WL_BYTES too big. -#endif -uint8_t eeprom_wl[EEPROM_WL_BYTES]; -uint8_t load_eeprom_wl(); // returns 1 for success, 0 for no data found -void save_eeprom_wl(); -#define EEP_WL_SIZE (EEPSIZE/2) -#endif - -#if EEPSIZE > 256 -#define EEP_OFFSET_T uint16_t -#else -#define EEP_OFFSET_T uint8_t -#endif - -// if this marker isn't found, the eeprom is assumed to be blank -#define EEP_MARKER 0b10100101 - diff --git a/spaghetti-monster/fsm-events.c b/spaghetti-monster/fsm-events.c deleted file mode 100644 index 6987ae2..0000000 --- a/spaghetti-monster/fsm-events.c +++ /dev/null @@ -1,198 +0,0 @@ -// fsm-events.c: Event-handling functions for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include - - -void append_emission(Event event, uint16_t arg) { - uint8_t i; - // find last entry - for(i=0; - (i=0; i--) { - uint8_t err = state_stack[i](event, arg); - if (! err) return 0; - } - return 1; // event not handled -} - -void emit(Event event, uint16_t arg) { - // add this event to the queue for later, - // so we won't use too much time during an interrupt - append_emission(event, arg); -} - -void emit_current_event(uint16_t arg) { - emit(current_event, arg); -} - -void empty_event_sequence() { - current_event = EV_none; - ticks_since_last_event = 0; - // when the user completes an input sequence, interrupt any running timers - // to cancel any delays currently in progress - // This eliminates a whole bunch of extra code: - // before: if (! nice_delay_ms(ms)) {break;} - // after: nice_delay_ms(ms); - interrupt_nice_delays(); -} - -uint8_t push_event(uint8_t ev_type) { // only for use by PCINT_inner() - // don't do this here; do it in PCINT_inner() instead - //ticks_since_last_event = 0; // something happened - - // only click events are sent to this function - current_event |= B_CLICK; - - // handle button presses - if (ev_type == B_PRESS) { - // set press flag - current_event |= B_PRESS; - // increase click counter - if ((current_event & B_COUNT) < (B_COUNT)) { - current_event ++; - } - return 1; // event pushed, even if max clicks already reached - // (will just repeat the max over and over) - } - // handle button releases - else if (ev_type == B_RELEASE) { - // clear the press flag - current_event &= (~B_PRESS); - // if a "hold" event just ended, set the timeout flag - // to indicate that the event is done and can be cleared - if (current_event & B_HOLD) { current_event |= B_TIMEOUT; } - return 1; // event pushed - } - - return 0; // unexpected event type -} - - -// explicitly interrupt these "nice" delays -volatile uint8_t nice_delay_interrupt = 0; -inline void interrupt_nice_delays() { nice_delay_interrupt = 1; } - -// like delay_ms, except it aborts on state change -// return value: -// 0: state changed -// 1: normal completion -uint8_t nice_delay_ms(uint16_t ms) { - /* // delay_zero() implementation - if (ms == 0) { - CLKPR = 1< 0) { - if (nice_delay_interrupt) { - return 0; - } - - #ifdef USE_DYNAMIC_UNDERCLOCKING - #ifdef USE_RAMPING - uint8_t level = actual_level; // volatile, avoid repeat access - if (level < QUARTERSPEED_LEVEL) { - clock_prescale_set(clock_div_4); - _delay_loop_2(BOGOMIPS*DELAY_FACTOR/100/4); - } - //else if (level < HALFSPEED_LEVEL) { - // clock_prescale_set(clock_div_2); - // _delay_loop_2(BOGOMIPS*95/100/2); - //} - else { - clock_prescale_set(clock_div_1); - _delay_loop_2(BOGOMIPS*DELAY_FACTOR/100); - } - // restore regular clock speed - clock_prescale_set(clock_div_1); - #else - // underclock MCU to save power - clock_prescale_set(clock_div_4); - // wait - _delay_loop_2(BOGOMIPS*DELAY_FACTOR/100/4); - // restore regular clock speed - clock_prescale_set(clock_div_1); - #endif // ifdef USE_RAMPING - #else - // wait - _delay_loop_2(BOGOMIPS*DELAY_FACTOR/100); - #endif // ifdef USE_DYNAMIC_UNDERCLOCKING - - // run pending system processes while we wait - handle_deferred_interrupts(); - - // handle events only afterward, so that any collapsed delays will - // finish running the UI's loop() code before taking any further actions - // (this helps make sure code runs in the correct order) - // (otherwise, a new state's EV_enter runs before the old state's - // loop() has finished, and things can get weird) - process_emissions(); - } - return 1; -} - -#ifdef USE_DYNAMIC_UNDERCLOCKING -void delay_4ms(uint8_t ms) { - while(ms-- > 0) { - // underclock MCU to save power - clock_prescale_set(clock_div_4); - // wait - _delay_loop_2(BOGOMIPS*98/100); - // restore regular clock speed - clock_prescale_set(clock_div_1); - } -} -#else -void delay_4ms(uint8_t ms) { - while(ms-- > 0) { - // wait - _delay_loop_2(BOGOMIPS*398/100); - } -} -#endif -/* -uint8_t nice_delay_4ms(uint8_t ms) { - return nice_delay_ms((uint16_t)ms << 2); -} -*/ - -/* -uint8_t nice_delay_s() { - return nice_delay_4ms(250); -} -*/ - diff --git a/spaghetti-monster/fsm-events.h b/spaghetti-monster/fsm-events.h deleted file mode 100644 index 575af1b..0000000 --- a/spaghetti-monster/fsm-events.h +++ /dev/null @@ -1,221 +0,0 @@ -// fsm-events.h: Event-handling functions for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include - - -// timeout durations in ticks (each tick 1/62th s) -#ifndef HOLD_TIMEOUT -#define HOLD_TIMEOUT 24 -#endif -#ifndef RELEASE_TIMEOUT -#define RELEASE_TIMEOUT 18 -#endif - -// return codes for Event handlers -// Indicates whether this handler consumed (handled) the Event, or -// if the Event should be sent to the next handler in the stack. -#define EVENT_HANDLED 0 -#define EVENT_NOT_HANDLED 1 -#define MISCHIEF_MANAGED EVENT_HANDLED -#define MISCHIEF_NOT_MANAGED EVENT_NOT_HANDLED - -// typedefs -typedef uint8_t Event; -typedef struct Emission { - Event event; - uint16_t arg; -} Emission; - -Event current_event; -// at 0.016 ms per tick, 255 ticks = 4.08 s -static volatile uint16_t ticks_since_last_event = 0; - -// maximum number of events which can be waiting at one time -// (would probably be okay to reduce this to 4, but it's higher to be safe) -#define EMISSION_QUEUE_LEN 16 -// was "volatile" before, changed to regular var since IRQ rewrites seem -// to have removed the need for it to be volatile -// no comment about "volatile emissions" -Emission emissions[EMISSION_QUEUE_LEN]; - -void append_emission(Event event, uint16_t arg); -void delete_first_emission(); -void process_emissions(); -uint8_t emit_now(Event event, uint16_t arg); -void emit(Event event, uint16_t arg); -void emit_current_event(uint16_t arg); -void empty_event_sequence(); -uint8_t push_event(uint8_t ev_type); // only for use by PCINT_inner() - - -// TODO: Maybe move these to their own file... -// ... this probably isn't the right place for delays. -#ifndef DELAY_FACTOR - // adjust the timing of delays, lower = shorter delays - // 90 = 90% delay, 10% for other things - #define DELAY_FACTOR 92 -#endif -inline void interrupt_nice_delays(); -uint8_t nice_delay_ms(uint16_t ms); -//uint8_t nice_delay_s(); -void delay_4ms(uint8_t ms); - - -/* Event structure - * Bit 7: 1 for a button input event, 0 for all others. - * If bit 7 is 1: - * Bits 0,1,2,3: Click counter. Up to 15 clicks. - * Bit 4: 1 for a "press" event, 0 for a "release" event. - * Bit 5: 1 for a "hold" event, 0 otherwise. - * Bit 6: 1 for a "timeout" event, 0 otherwise. - * If bit 7 is 0: - * Sort of ad-hoc, shown in #defines below. - */ - -// event masks / bits -#define B_SYSTEM 0b00000000 -#define B_CLICK 0b10000000 -#define B_TIMEOUT 0b01000000 -#define B_HOLD 0b00100000 -#define B_PRESS 0b00010000 -#define B_RELEASE 0b00000000 -#define B_COUNT 0b00001111 -#define B_FLAGS 0b11110000 - -// Event types -#define EV_none 0 - -// Events which aren't button presses -#define EV_debug (B_SYSTEM|0b01111111) -#define EV_enter_state (B_SYSTEM|0b00001000) -#define EV_leave_state (B_SYSTEM|0b00001001) -#define EV_reenter_state (B_SYSTEM|0b00001010) -#define EV_tick (B_SYSTEM|0b00000001) -#ifdef TICK_DURING_STANDBY -#define EV_sleep_tick (B_SYSTEM|0b00000011) -#endif -#ifdef USE_LVP -#define EV_voltage_low (B_SYSTEM|0b00000100) -#endif -#ifdef USE_THERMAL_REGULATION -#define EV_temperature_high (B_SYSTEM|0b00000101) -#define EV_temperature_low (B_SYSTEM|0b00000110) -#define EV_temperature_okay (B_SYSTEM|0b00000111) -#endif - -// Button press events - -// shouldn't normally happen, but UI might empty_event_sequence() while button -// is down so a release with no recorded prior hold could be possible -#define EV_release (B_CLICK|B_RELEASE|0) - -#define EV_click1_press (B_CLICK|B_PRESS|1) -#define EV_click1_release (B_CLICK|B_RELEASE|1) -#define EV_click1_complete (B_CLICK|B_TIMEOUT|1) -#define EV_1click EV_click1_complete -#define EV_click1_hold (B_CLICK|B_HOLD|B_PRESS|1) -#define EV_click1_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|1) -#define EV_hold EV_click1_hold - -#define EV_click2_press (B_CLICK|B_PRESS|2) -#define EV_click2_release (B_CLICK|B_RELEASE|2) -#define EV_click2_complete (B_CLICK|B_TIMEOUT|2) -#define EV_2clicks EV_click2_complete -#define EV_click2_hold (B_CLICK|B_HOLD|B_PRESS|2) -#define EV_click2_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|2) - -#define EV_click3_press (B_CLICK|B_PRESS|3) -#define EV_click3_release (B_CLICK|B_RELEASE|3) -#define EV_click3_complete (B_CLICK|B_TIMEOUT|3) -#define EV_3clicks EV_click3_complete -#define EV_click3_hold (B_CLICK|B_HOLD|B_PRESS|3) -#define EV_click3_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|3) - -#define EV_click4_press (B_CLICK|B_PRESS|4) -#define EV_click4_release (B_CLICK|B_RELEASE|4) -#define EV_click4_complete (B_CLICK|B_TIMEOUT|4) -#define EV_4clicks EV_click4_complete -#define EV_click4_hold (B_CLICK|B_HOLD|B_PRESS|4) -#define EV_click4_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|4) - -#define EV_click5_press (B_CLICK|B_PRESS|5) -#define EV_click5_release (B_CLICK|B_RELEASE|5) -#define EV_click5_complete (B_CLICK|B_TIMEOUT|5) -#define EV_5clicks EV_click5_complete -#define EV_click5_hold (B_CLICK|B_HOLD|B_PRESS|5) -#define EV_click5_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|5) - -#define EV_click6_press (B_CLICK|B_PRESS|6) -#define EV_click6_release (B_CLICK|B_RELEASE|6) -#define EV_click6_complete (B_CLICK|B_TIMEOUT|6) -#define EV_6clicks EV_click6_complete -#define EV_click6_hold (B_CLICK|B_HOLD|B_PRESS|6) -#define EV_click6_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|6) - -#define EV_click7_press (B_CLICK|B_PRESS|7) -#define EV_click7_release (B_CLICK|B_RELEASE|7) -#define EV_click7_complete (B_CLICK|B_TIMEOUT|7) -#define EV_7clicks EV_click7_complete -#define EV_click7_hold (B_CLICK|B_HOLD|B_PRESS|7) -#define EV_click7_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|7) - -#define EV_click8_press (B_CLICK|B_PRESS|8) -#define EV_click8_release (B_CLICK|B_RELEASE|8) -#define EV_click8_complete (B_CLICK|B_TIMEOUT|8) -#define EV_8clicks EV_click8_complete -#define EV_click8_hold (B_CLICK|B_HOLD|B_PRESS|8) -#define EV_click8_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|8) - -#define EV_click9_press (B_CLICK|B_PRESS|9) -#define EV_click9_release (B_CLICK|B_RELEASE|9) -#define EV_click9_complete (B_CLICK|B_TIMEOUT|9) -#define EV_9clicks EV_click9_complete -#define EV_click9_hold (B_CLICK|B_HOLD|B_PRESS|9) -#define EV_click9_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|9) - -#define EV_click10_press (B_CLICK|B_PRESS|10) -#define EV_click10_release (B_CLICK|B_RELEASE|10) -#define EV_click10_complete (B_CLICK|B_TIMEOUT|10) -#define EV_10clicks EV_click10_complete -#define EV_click10_hold (B_CLICK|B_HOLD|B_PRESS|10) -#define EV_click10_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|10) - -#define EV_click11_press (B_CLICK|B_PRESS|11) -#define EV_click11_release (B_CLICK|B_RELEASE|11) -#define EV_click11_complete (B_CLICK|B_TIMEOUT|11) -#define EV_11clicks EV_click11_complete -#define EV_click11_hold (B_CLICK|B_HOLD|B_PRESS|11) -#define EV_click11_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|11) - -#define EV_click12_press (B_CLICK|B_PRESS|12) -#define EV_click12_release (B_CLICK|B_RELEASE|12) -#define EV_click12_complete (B_CLICK|B_TIMEOUT|12) -#define EV_12clicks EV_click12_complete -#define EV_click12_hold (B_CLICK|B_HOLD|B_PRESS|12) -#define EV_click12_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|12) - -#define EV_click13_press (B_CLICK|B_PRESS|13) -#define EV_click13_release (B_CLICK|B_RELEASE|13) -#define EV_click13_complete (B_CLICK|B_TIMEOUT|13) -#define EV_13clicks EV_click13_complete -#define EV_click13_hold (B_CLICK|B_HOLD|B_PRESS|13) -#define EV_click13_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|13) - -#define EV_click14_press (B_CLICK|B_PRESS|14) -#define EV_click14_release (B_CLICK|B_RELEASE|14) -#define EV_click14_complete (B_CLICK|B_TIMEOUT|14) -#define EV_14clicks EV_click14_complete -#define EV_click14_hold (B_CLICK|B_HOLD|B_PRESS|14) -#define EV_click14_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|14) - -#define EV_click15_press (B_CLICK|B_PRESS|15) -#define EV_click15_release (B_CLICK|B_RELEASE|15) -#define EV_click15_complete (B_CLICK|B_TIMEOUT|15) -#define EV_15clicks EV_click15_complete -#define EV_click15_hold (B_CLICK|B_HOLD|B_PRESS|15) -#define EV_click15_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|15) - diff --git a/spaghetti-monster/fsm-main.c b/spaghetti-monster/fsm-main.c deleted file mode 100644 index 066188c..0000000 --- a/spaghetti-monster/fsm-main.c +++ /dev/null @@ -1,211 +0,0 @@ -// fsm-main.c: main() function for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "fsm-main.h" - -#if PWM_CHANNELS == 4 -#ifdef AVRXMEGA3 // ATTINY816, 817, etc -#error 4-channel PWM not currently set up for the AVR 1-Series -#endif -// 4th PWM channel requires manually turning the pin on/off via interrupt :( -ISR(TIMER1_OVF_vect) { - //bitClear(PORTB, 3); - PORTB &= 0b11110111; - //PORTB |= 0b00001000; -} -ISR(TIMER1_COMPA_vect) { - //if (!bitRead(TIFR,TOV1)) bitSet(PORTB, 3); - if (! (TIFR & (1<= 1 - DDRB |= (1 << PWM1_PIN); - TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) - TCCR0A = PHASE; - #if (PWM1_PIN == PB4) // Second PWM counter is ... weird - TCCR1 = _BV (CS10); - GTCCR = _BV (COM1B1) | _BV (PWM1B); - OCR1C = 255; // Set ceiling value to maximum - #endif - #endif - // tint ramping needs second channel enabled, - // despite PWM_CHANNELS being only 1 - #if (PWM_CHANNELS >= 2) || defined(USE_TINT_RAMPING) - DDRB |= (1 << PWM2_PIN); - #if (PWM2_PIN == PB4) // Second PWM counter is ... weird - TCCR1 = _BV (CS10); - GTCCR = _BV (COM1B1) | _BV (PWM1B); - OCR1C = 255; // Set ceiling value to maximum - #endif - #endif - #if PWM_CHANNELS >= 3 - DDRB |= (1 << PWM3_PIN); - #if (PWM3_PIN == PB4) // Second PWM counter is ... weird - TCCR1 = _BV (CS10); - GTCCR = _BV (COM1B1) | _BV (PWM1B); - OCR1C = 255; // Set ceiling value to maximum - #endif - #endif - #if PWM_CHANNELS >= 4 - // 4th PWM channel is ... not actually supported in hardware :( - DDRB |= (1 << PWM4_PIN); - //OCR1C = 255; // Set ceiling value to maximum - TCCR1 = 1<= 1 - PWM1_LVL = 0; - #endif - #if PWM_CHANNELS >= 2 - PWM2_LVL = 0; - #endif - #if PWM_CHANNELS >= 3 - PWM3_LVL = 0; - #endif - #if PWM_CHANNELS >= 4 - PWM4_LVL = 255; // inverted :( - #endif - #endif - standby_mode(); - } - - // catch up on interrupts - handle_deferred_interrupts(); - - // turn delays back on, if they were off - nice_delay_interrupt = 0; - - // give the recipe some time slices - loop(); - - } -} - - -void handle_deferred_interrupts() { - /* - if (irq_pcint) { // button pressed or released - // nothing to do here - // (PCINT only matters during standby) - } - */ - if (irq_adc) { // ADC done measuring - adc_deferred(); - // irq_adc = 0; // takes care of itself - } - if (irq_wdt) { // the clock ticked - WDT_inner(); - // irq_wdt = 0; // takes care of itself - } -} - diff --git a/spaghetti-monster/fsm-main.h b/spaghetti-monster/fsm-main.h deleted file mode 100644 index 2e2a111..0000000 --- a/spaghetti-monster/fsm-main.h +++ /dev/null @@ -1,10 +0,0 @@ -// fsm-main.h: main() function for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -int main(); -// needs to run frequently to execute the logic for WDT and ADC and stuff -void handle_deferred_interrupts(); - diff --git a/spaghetti-monster/fsm-misc.c b/spaghetti-monster/fsm-misc.c deleted file mode 100644 index bc10ea1..0000000 --- a/spaghetti-monster/fsm-misc.c +++ /dev/null @@ -1,312 +0,0 @@ -// fsm-misc.c: Miscellaneous function for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#ifdef USE_DYNAMIC_UNDERCLOCKING -void auto_clock_speed() { - uint8_t level = actual_level; // volatile, avoid repeat access - if (level < QUARTERSPEED_LEVEL) { - // run at quarter speed - // note: this only works when executed as two consecutive instructions - // (don't try to combine them or put other stuff between) - clock_prescale_set(clock_div_4); - } - else if (level < HALFSPEED_LEVEL) { - // run at half speed - clock_prescale_set(clock_div_2); - } else { - // run at full speed - clock_prescale_set(clock_div_1); - } -} -#endif - -#if defined(USE_BLINK_NUM) || defined(USE_BLINK_DIGIT) -#define BLINK_SPEED 1000 -uint8_t blink_digit(uint8_t num) { - //StatePtr old_state = current_state; - - // "zero" digit gets a single short blink - uint8_t ontime = BLINK_SPEED * 2 / 12; - if (!num) { ontime = BLINK_ONCE_TIME; num ++; } - - #ifdef BLINK_CHANNEL - // channel is set per blink, to prevent issues - // if another mode interrupts us (like a config menu) - uint8_t old_channel = channel_mode; - #endif - - for (; num>0; num--) { - // TODO: allow setting a blink channel mode per build target - #ifdef BLINK_CHANNEL - set_channel_mode(BLINK_CHANNEL); - #endif - set_level(BLINK_BRIGHTNESS); - #ifdef BLINK_CHANNEL - channel_mode = old_channel; - #endif - nice_delay_ms(ontime); - - #ifdef BLINK_CHANNEL - set_channel_mode(BLINK_CHANNEL); - #endif - set_level(0); - #ifdef BLINK_CHANNEL - channel_mode = old_channel; - #endif - nice_delay_ms(BLINK_SPEED * 3 / 12); - } - - #ifdef BLINK_CHANNEL - set_channel_mode(old_channel); - #endif - - return nice_delay_ms(BLINK_SPEED * 8 / 12); -} -#endif - -#ifdef USE_BLINK_BIG_NUM -uint8_t blink_big_num(uint16_t num) { - uint16_t digits[] = { 10000, 1000, 100, 10, 1 }; - uint8_t started = 0; - for (uint8_t digit=0; digit= scale) { - started = 1; - } - if (started) { - uint8_t digit = 0; - while (num >= scale) { - num -= scale; - digit ++; - } - if (! blink_digit(digit)) return 0; - } - } - - return nice_delay_ms(1000); -} -#endif -#ifdef USE_BLINK_NUM -uint8_t blink_num(uint8_t num) { - #if 1 - uint8_t hundreds = num / 100; - num = num % 100; - uint8_t tens = num / 10; - num = num % 10; - #else // can be smaller or larger, depending on whether divmod is used elsewhere - uint8_t hundreds = 0; - uint8_t tens = 0; - for(; num >= 100; hundreds ++, num -= 100); - for(; num >= 10; tens ++, num -= 10); - #endif - - #if 0 - // wait a moment in the dark before starting - set_level(0); - nice_delay_ms(200); - #endif - - if (hundreds) blink_digit(hundreds); - if (hundreds || tens) blink_digit(tens); - return blink_digit(num); -} -#endif - -#ifdef USE_INDICATOR_LED -void indicator_led(uint8_t lvl) { - switch (lvl) { - #ifdef AVRXMEGA3 // ATTINY816, 817, etc - - case 0: // indicator off - AUXLED_PORT.DIRSET = (1 << AUXLED_PIN); // set as output - AUXLED_PORT.OUTCLR = (1 << AUXLED_PIN); // set output low - #ifdef AUXLED2_PIN // second LED mirrors the first - AUXLED2_PORT.DIRSET = (1 << AUXLED2_PIN); // set as output - AUXLED2_PORT.OUTCLR = (1 << AUXLED2_PIN); // set output low - #endif - break; - case 1: // indicator low - AUXLED_PORT.DIRCLR = (1 << AUXLED_PIN); // set as input - // this resolves to PORTx.PINxCTRL = PORT_PULLUPEN_bm; - *((uint8_t *)&AUXLED_PORT + 0x10 + AUXLED_PIN) = PORT_PULLUPEN_bm; // enable internal pull-up - #ifdef AUXLED2_PIN // second LED mirrors the first - AUXLED2_PORT.DIRCLR = (1 << AUXLED2_PIN); // set as input - // this resolves to PORTx.PINxCTRL = PORT_PULLUPEN_bm; - *((uint8_t *)&AUXLED2_PORT + 0x10 + AUXLED2_PIN) = PORT_PULLUPEN_bm; // enable internal pull-up - #endif - break; - default: // indicator high - AUXLED_PORT.DIRSET = (1 << AUXLED_PIN); // set as output - AUXLED_PORT.OUTSET = (1 << AUXLED_PIN); // set as high - #ifdef AUXLED2_PIN // second LED mirrors the first - AUXLED2_PORT.DIRSET = (1 << AUXLED2_PIN); // set as output - AUXLED2_PORT.OUTSET = (1 << AUXLED2_PIN); // set as high - #endif - break; - - #else // MCU is old tiny style, not newer mega style - - case 0: // indicator off - DDRB &= 0xff ^ (1 << AUXLED_PIN); - PORTB &= 0xff ^ (1 << AUXLED_PIN); - #ifdef AUXLED2_PIN // second LED mirrors the first - DDRB &= 0xff ^ (1 << AUXLED2_PIN); - PORTB &= 0xff ^ (1 << AUXLED2_PIN); - #endif - break; - case 1: // indicator low - DDRB &= 0xff ^ (1 << AUXLED_PIN); - PORTB |= (1 << AUXLED_PIN); - #ifdef AUXLED2_PIN // second LED mirrors the first - DDRB &= 0xff ^ (1 << AUXLED2_PIN); - PORTB |= (1 << AUXLED2_PIN); - #endif - break; - default: // indicator high - DDRB |= (1 << AUXLED_PIN); - PORTB |= (1 << AUXLED_PIN); - #ifdef AUXLED2_PIN // second LED mirrors the first - DDRB |= (1 << AUXLED2_PIN); - PORTB |= (1 << AUXLED2_PIN); - #endif - break; - - #endif // MCU type - } -} - -/* -void indicator_led_auto() { - if (actual_level > MAX_1x7135) indicator_led(2); - else if (actual_level > 0) indicator_led(1); - else indicator_led(0); -} -*/ -#endif // USE_INDICATOR_LED - -#ifdef USE_BUTTON_LED -// TODO: Refactor this and RGB LED function to merge code and save space -void button_led_set(uint8_t lvl) { - switch (lvl) { - - #ifdef AVRXMEGA3 // ATTINY816, 817, etc - - case 0: // LED off - BUTTON_LED_PORT.DIRSET = (1 << BUTTON_LED_PIN); // set as output - BUTTON_LED_PORT.OUTCLR = (1 << BUTTON_LED_PIN); // set output low - break; - case 1: // LED low - BUTTON_LED_PORT.DIRCLR = (1 << BUTTON_LED_PIN); // set as input - // this resolves to PORTx.PINxCTRL = PORT_PULLUPEN_bm; - *((uint8_t *)&BUTTON_LED_PORT + 0x10 + BUTTON_LED_PIN) = PORT_PULLUPEN_bm; // enable internal pull-up - break; - default: // LED high - BUTTON_LED_PORT.DIRSET = (1 << BUTTON_LED_PIN); // set as output - BUTTON_LED_PORT.OUTSET = (1 << BUTTON_LED_PIN); // set as high - break; - - #else - - case 0: // LED off - BUTTON_LED_DDR &= 0xff ^ (1 << BUTTON_LED_PIN); - BUTTON_LED_PUE &= 0xff ^ (1 << BUTTON_LED_PIN); - BUTTON_LED_PORT &= 0xff ^ (1 << BUTTON_LED_PIN); - break; - case 1: // LED low - BUTTON_LED_DDR &= 0xff ^ (1 << BUTTON_LED_PIN); - BUTTON_LED_PUE |= (1 << BUTTON_LED_PIN); - BUTTON_LED_PORT |= (1 << BUTTON_LED_PIN); - break; - default: // LED high - BUTTON_LED_DDR |= (1 << BUTTON_LED_PIN); - BUTTON_LED_PUE |= (1 << BUTTON_LED_PIN); - BUTTON_LED_PORT |= (1 << BUTTON_LED_PIN); - break; - - #endif // MCU type - } -} -#endif - -#ifdef USE_AUX_RGB_LEDS -void rgb_led_set(uint8_t value) { - // value: 0b00BBGGRR - uint8_t pins[] = { AUXLED_R_PIN, AUXLED_G_PIN, AUXLED_B_PIN }; - for (uint8_t i=0; i<3; i++) { - uint8_t lvl = (value >> (i<<1)) & 0x03; - uint8_t pin = pins[i]; - switch (lvl) { - - #ifdef AVRXMEGA3 // ATTINY816, 817, etc - - case 0: // LED off - AUXLED_RGB_PORT.DIRSET = (1 << pin); // set as output - AUXLED_RGB_PORT.OUTCLR = (1 << pin); // set output low - break; - case 1: // LED low - AUXLED_RGB_PORT.DIRCLR = (1 << pin); // set as input - // this resolves to PORTx.PINxCTRL = PORT_PULLUPEN_bm; - *((uint8_t *)&AUXLED_RGB_PORT + 0x10 + pin) = PORT_PULLUPEN_bm; // enable internal pull-up - break; - default: // LED high - AUXLED_RGB_PORT.DIRSET = (1 << pin); // set as output - AUXLED_RGB_PORT.OUTSET = (1 << pin); // set as high - break; - - #else - - case 0: // LED off - AUXLED_RGB_DDR &= 0xff ^ (1 << pin); - AUXLED_RGB_PUE &= 0xff ^ (1 << pin); - AUXLED_RGB_PORT &= 0xff ^ (1 << pin); - break; - case 1: // LED low - AUXLED_RGB_DDR &= 0xff ^ (1 << pin); - AUXLED_RGB_PUE |= (1 << pin); - AUXLED_RGB_PORT |= (1 << pin); - break; - default: // LED high - AUXLED_RGB_DDR |= (1 << pin); - AUXLED_RGB_PUE |= (1 << pin); - AUXLED_RGB_PORT |= (1 << pin); - break; - - #endif // MCU type - } - } -} -#endif // ifdef USE_AUX_RGB_LEDS - -#ifdef USE_TRIANGLE_WAVE -uint8_t triangle_wave(uint8_t phase) { - uint8_t result = phase << 1; - if (phase > 127) result = 255 - result; - return result; -} -#endif - -#ifdef USE_REBOOT -void reboot() { - // put the WDT in hard reset mode, then trigger it - cli(); - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) - WDTCR = 0xD8 | WDTO_15MS; - #elif (ATTINY == 1634) - // allow protected configuration changes for next 4 clock cycles - CCP = 0xD8; // magic number - // reset (WDIF + WDE), no WDIE, fastest (16ms) timing (0000) - // (DS section 8.5.2 and table 8-4) - WDTCSR = 0b10001000; - #elif defined(AVRXMEGA3) // ATTINY816, 817, etc - CCP = CCP_IOREG_gc; // temporarily disable change protection - WDT.CTRLA = WDT_PERIOD_8CLK_gc; // Enable, timeout 8ms - #endif - sei(); - wdt_reset(); - while (1) {} -} -#endif - diff --git a/spaghetti-monster/fsm-misc.h b/spaghetti-monster/fsm-misc.h deleted file mode 100644 index 8de6b29..0000000 --- a/spaghetti-monster/fsm-misc.h +++ /dev/null @@ -1,68 +0,0 @@ -// fsm-misc.h: Miscellaneous function for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#ifdef USE_DYNAMIC_UNDERCLOCKING -void auto_clock_speed(); -#endif - -// shortest time (in ms) the light should blink for to indicate a zero -#ifndef BLINK_ONCE_TIME - #define BLINK_ONCE_TIME 10 -#endif - -#if defined(USE_BLINK_NUM) || defined(USE_BLINK_DIGIT) - #ifndef BLINK_BRIGHTNESS - #define BLINK_BRIGHTNESS (MAX_LEVEL/6) - #endif - #if defined(USE_CFG) && defined(DEFAULT_BLINK_CHANNEL) - #define BLINK_CHANNEL cfg.blink_channel - #elif defined(DEFAULT_BLINK_CHANNEL) - #define BLINK_CHANNEL DEFAULT_BLINK_CHANNEL - #endif - uint8_t blink_digit(uint8_t num); -#endif - -#ifdef USE_BLINK_NUM -//#define USE_BLINK -uint8_t blink_num(uint8_t num); -#endif - -/* -#ifdef USE_BLINK -uint8_t blink(uint8_t num, uint8_t speed); -#endif -*/ - -#ifdef USE_INDICATOR_LED -// FIXME: Remove this, replace with button_led() -// lvl: 0=off, 1=low, 2=high -void indicator_led(uint8_t lvl); -#endif - -#ifdef USE_BUTTON_LED -// lvl: 0=off, 1=low, 2=high -void button_led_set(uint8_t lvl); -#endif - -// if any type of aux LEDs exist, define a shorthand flag for it -#if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS) || defined(USE_BUTTON_LED) -#define HAS_AUX_LEDS -#endif - -#ifdef USE_AUX_RGB_LEDS -// value: 0b00BBGGRR -// each pair of bits: 0=off, 1=low, 2=high -void rgb_led_set(uint8_t value); -#endif - -#ifdef USE_TRIANGLE_WAVE -uint8_t triangle_wave(uint8_t phase); -#endif - -#ifdef USE_REBOOT -void reboot(); -#endif - diff --git a/spaghetti-monster/fsm-pcint.c b/spaghetti-monster/fsm-pcint.c deleted file mode 100644 index 131d0c3..0000000 --- a/spaghetti-monster/fsm-pcint.c +++ /dev/null @@ -1,96 +0,0 @@ -// fsm-pcint.c: PCINT (Pin Change Interrupt) functions for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include -#include - -uint8_t button_is_pressed() { - uint8_t value = ((SWITCH_PORT & (1< 0) + (level > DEFAULT_LEVEL)); - #endif - #ifdef USE_BUTTON_LED - button_led_set((level > 0) + (level > DEFAULT_LEVEL)); - #endif - } - #else // turn off front-facing aux LEDs while main LEDs are on - #if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS) - if (! go_to_standby) { - #ifdef USE_INDICATOR_LED - indicator_led(0); - #endif - #ifdef USE_AUX_RGB_LEDS - rgb_led_set(0); - #ifdef USE_BUTTON_LED - button_led_set((level > 0) + (level > DEFAULT_LEVEL)); - #endif - #endif - } - #endif - #endif -} -#endif // ifdef HAS_AUX_LEDS - -#ifdef USE_AUX_RGB_LEDS_WHILE_ON -// TODO: maybe move this stuff into FSM -#include "anduril/aux-leds.h" // for rgb_led_voltage_readout() -inline void set_level_aux_rgb_leds(uint8_t level) { - if (! go_to_standby) { - if (level > 0) { - rgb_led_voltage_readout(level > USE_AUX_RGB_LEDS_WHILE_ON); - } else { - rgb_led_set(0); - } - // some drivers can be wired with RGB or single color to button - // ... so support both even though only one is connected - #ifdef USE_BUTTON_LED - button_led_set((level > 0) + (level > DEFAULT_LEVEL)); - #endif - } -} -#endif // ifdef USE_AUX_RGB_LEDS_WHILE_ON - - -void set_level(uint8_t level) { - #ifdef USE_JUMP_START - // maybe "jump start" the engine, if it's prone to slow starts - // (pulse the output high for a moment to wake up the power regulator) - // (only do this when starting from off and going to a low level) - // TODO: allow different jump start behavior per channel mode - // FIXME: don't jump-start during factory reset - // (it seems to cause some eeprom issues on KR4 - // when doing a click with a loose tailcap) - if ((! actual_level) - && level - && (level < JUMP_START_LEVEL)) { - set_level(JUMP_START_LEVEL); - delay_4ms(JUMP_START_TIME/4); - } - #endif - - #ifdef HAS_AUX_LEDS - set_level_aux_leds(level); - #endif - - #ifdef USE_AUX_RGB_LEDS_WHILE_ON - set_level_aux_rgb_leds(level); - #endif - - if (0 == level) { - set_level_zero(); - } else { - // call the relevant hardware-specific set_level_*() - SetLevelFuncPtr set_level_func = channels[channel_mode].set_level; - set_level_func(level - 1); - } - - if (actual_level != level) prev_level = actual_level; - actual_level = level; - - #ifdef USE_SET_LEVEL_GRADUALLY - gradual_target = level; - #endif - - #ifdef USE_DYNAMIC_UNDERCLOCKING - auto_clock_speed(); - #endif -} - -#ifdef USE_LEGACY_SET_LEVEL -// (this is mostly just here for reference, temporarily) -// single set of LEDs with 1 to 3 stacked power channels, -// like linear, FET+1, and FET+N+1 -// (default set_level_*() function for most lights) -void set_level_legacy(uint8_t level) { - if (level == 0) { - #if PWM_CHANNELS >= 1 - PWM1_LVL = 0; - #endif - #if PWM_CHANNELS >= 2 - PWM2_LVL = 0; - #endif - #if PWM_CHANNELS >= 3 - PWM3_LVL = 0; - #endif - #if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_OFF) - PWM1_CNT = 0; - #endif - #if defined(PWM2_CNT) && defined(PWM2_PHASE_RESET_OFF) - PWM2_CNT = 0; - #endif - #if defined(PWM3_CNT) && defined(PWM3_PHASE_RESET_OFF) - PWM3_CNT = 0; - #endif - #ifdef LED_OFF_DELAY - // for drivers with a slow regulator chip (eg, boost converter), - // delay before turning off to prevent flashes - delay_4ms(LED_OFF_DELAY/4); - #endif - // disable the power channel, if relevant - #ifdef LED_ENABLE_PIN - LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN); - #endif - #ifdef LED2_ENABLE_PIN - LED2_ENABLE_PORT &= ~(1 << LED2_ENABLE_PIN); - #endif - } else { - // enable the power channel, if relevant - #ifdef LED_ENABLE_PIN - #ifdef LED_ON_DELAY - uint8_t led_enable_port_save = LED_ENABLE_PORT; - #endif - - #ifndef LED_ENABLE_PIN_LEVEL_MIN - LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN); - #else - // only enable during part of the ramp - if ((level >= LED_ENABLE_PIN_LEVEL_MIN) - && (level <= LED_ENABLE_PIN_LEVEL_MAX)) - LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN); - else // disable during other parts of the ramp - LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN); - #endif - - // for drivers with a slow regulator chip (eg, boost converter), - // delay before lighting up to prevent flashes - #ifdef LED_ON_DELAY - // only delay if the pin status changed - if (LED_ENABLE_PORT != led_enable_port_save) - delay_4ms(LED_ON_DELAY/4); - #endif - #endif - #ifdef LED2_ENABLE_PIN - #ifdef LED2_ON_DELAY - uint8_t led2_enable_port_save = LED2_ENABLE_PORT; - #endif - - LED2_ENABLE_PORT |= (1 << LED2_ENABLE_PIN); - - // for drivers with a slow regulator chip (eg, boost converter), - // delay before lighting up to prevent flashes - #ifdef LED2_ON_DELAY - // only delay if the pin status changed - if (LED2_ENABLE_PORT != led2_enable_port_save) - delay_4ms(LED2_ON_DELAY/4); - #endif - #endif - - // PWM array index = level - 1 - level --; - - #if PWM_CHANNELS >= 1 - PWM1_LVL = PWM_GET(pwm1_levels, level); - #endif - #if PWM_CHANNELS >= 2 - PWM2_LVL = PWM_GET(pwm2_levels, level); - #endif - #if PWM_CHANNELS >= 3 - PWM3_LVL = PWM_GET(pwm3_levels, level); - #endif - - #ifdef USE_DYN_PWM - uint16_t top = PWM_GET(pwm_tops, level); - #if defined(PWM1_CNT) && defined(PWM1_PHASE_SYNC) - // wait to ensure compare match won't be missed - // (causes visible flickering when missed, because the counter - // goes all the way to 65535 before returning) - // (see attiny1634 reference manual page 103 for a warning about - // the timing of changing the TOP value (section 12.8.4)) - // (but don't wait when turning on from zero, because - // it'll reset the phase below anyway) - // to be safe, allow at least 32 cycles to update TOP - while(actual_level && (PWM1_CNT > (top - 32))) {} - #endif - // pulse frequency modulation, a.k.a. dynamic PWM - PWM1_TOP = top; - #endif // ifdef USE_DYN_PWM - #if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_ON) - // force reset phase when turning on from zero - // (because otherwise the initial response is inconsistent) - if (! actual_level) { - PWM1_CNT = 0; - #if defined(PWM2_CNT) && defined(PWM2_PHASE_RESET_ON) - PWM2_CNT = 0; - #endif - #if defined(PWM3_CNT) && defined(PWM3_PHASE_RESET_ON) - PWM3_CNT = 0; - #endif - } - #endif - } - #ifdef USE_DYNAMIC_UNDERCLOCKING - auto_clock_speed(); - #endif -} -#endif - - -#ifdef USE_SET_LEVEL_GRADUALLY -inline void set_level_gradually(uint8_t lvl) { - gradual_target = lvl; -} - - -// call this every frame or every few frames to change brightness very smoothly -void gradual_tick() { - uint8_t gt = gradual_target; - if (gt < actual_level) gt = actual_level - 1; - else if (gt > actual_level) gt = actual_level + 1; - - // call the relevant hardware-specific function - GradualTickFuncPtr gradual_tick_func = channels[channel_mode].gradual_tick; - bool done = gradual_tick_func(gt - 1); - - if (done) { - uint8_t orig = gradual_target; - set_level(gt); - gradual_target = orig; - } -} -#endif // ifdef USE_SET_LEVEL_GRADUALLY - - -#endif // ifdef USE_RAMPING - diff --git a/spaghetti-monster/fsm-ramping.h b/spaghetti-monster/fsm-ramping.h deleted file mode 100644 index c4b7d48..0000000 --- a/spaghetti-monster/fsm-ramping.h +++ /dev/null @@ -1,167 +0,0 @@ -// fsm-ramping.h: Ramping functions for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#ifdef USE_RAMPING - -// actual_level: last ramp level set by set_level() -uint8_t actual_level = 0; -// the level used before actual -uint8_t prev_level = 0; - -void set_level(uint8_t level); -//void set_level_smooth(uint8_t level); -void set_level_zero(); // implement this in a hwdef - -#ifdef USE_SET_LEVEL_GRADUALLY -// adjust brightness very smoothly -uint8_t gradual_target; -inline void set_level_gradually(uint8_t lvl); -void gradual_tick(); - -// reduce repetition with macros -#define GRADUAL_TICK_SETUP() \ - PWM_DATATYPE target; - -// tick to a specific value -#define GRADUAL_ADJUST_SIMPLE(TARGET,PWM) \ - if (PWM < TARGET) PWM ++; \ - else if (PWM > TARGET) PWM --; - -// tick to a specific value, except when immediate 0 to 255 is needed -#define GRADUAL_ADJUST_STACKED(TARGET,PWM,TOP) \ - if ( ((PWM == 0) && (TARGET == TOP)) \ - || ((PWM == TOP) && (TARGET == 0))) \ - PWM = TARGET; \ - else GRADUAL_ADJUST_SIMPLE(TARGET,PWM) - -// tick the top layer of the stack -#define GRADUAL_ADJUST_1CH(TABLE,PWM) \ - target = PWM_GET(TABLE, gt); \ - if (PWM < target) PWM ++; \ - else if (PWM > target) PWM --; - -// tick a base level of the stack -// (with support for special DD FET behavior -// like "low=0, high=255" --> "low=255, high=254") -#define GRADUAL_ADJUST(TABLE,PWM,TOP) \ - target = PWM_GET(TABLE, gt); \ - if ((gt < actual_level) \ - && (PWM == 0) \ - && (target == TOP)) PWM = TOP; \ - else \ - if (PWM < target) PWM ++; \ - else if (PWM > target) PWM --; - -#endif // ifdef USE_SET_LEVEL_GRADUALLY - -// auto-detect the data type for PWM tables -// FIXME: PWM bits and data type should be per PWM table -// FIXME: this whole thing is a mess and should be removed -#ifndef PWM1_BITS - #define PWM1_BITS 8 - #define PWM1_TOP 255 - #define STACKED_PWM_TOP 255 -#endif -#if PWM_BITS <= 8 - #define STACKED_PWM_DATATYPE uint8_t - #define PWM_DATATYPE uint8_t - #define PWM_DATATYPE2 uint16_t - #ifndef PWM_TOP - #define PWM_TOP 255 - #endif - #define STACKED_PWM_TOP 255 - #ifndef PWM_GET - #define PWM_GET(x,y) pgm_read_byte(x+y) - #endif -#else - #define STACKED_PWM_DATATYPE uint16_t - #define PWM_DATATYPE uint16_t - #ifndef PWM_DATATYPE2 - #define PWM_DATATYPE2 uint32_t - #endif - #ifndef PWM_TOP - #define PWM_TOP 1023 // 10 bits by default - #endif - #ifndef STACKED_PWM_TOP - #define STACKED_PWM_TOP 1023 - #endif - // pointer plus 2*y bytes - //#define PWM_GET(x,y) pgm_read_word(x+(2*y)) - // nope, the compiler was already doing the math correctly - #ifndef PWM_GET - #define PWM_GET(x,y) pgm_read_word(x+y) - #endif -#endif -#define PWM_GET8(x,y) pgm_read_byte(x+y) -#define PWM_GET16(x,y) pgm_read_word(x+y) - -// use UI-defined ramp tables if they exist -#ifdef PWM1_LEVELS -PROGMEM const PWM1_DATATYPE pwm1_levels[] = { PWM1_LEVELS }; -#endif -#ifdef PWM2_LEVELS -PROGMEM const PWM2_DATATYPE pwm2_levels[] = { PWM2_LEVELS }; -#endif -#ifdef PWM3_LEVELS -PROGMEM const PWM3_DATATYPE pwm3_levels[] = { PWM3_LEVELS }; -#endif -#ifdef PWM4_LEVELS -PROGMEM const PWM4_DATATYPE pwm4_levels[] = { PWM4_LEVELS }; -#endif -#ifdef PWM5_LEVELS -PROGMEM const PWM5_DATATYPE pwm5_levels[] = { PWM5_LEVELS }; -#endif - -// convenience defs for 1 LED with stacked channels -// FIXME: remove this, use pwm1/2/3 instead -#ifdef LOW_PWM_LEVELS -PROGMEM const PWM_DATATYPE low_pwm_levels[] = { LOW_PWM_LEVELS }; -#endif -#ifdef MED_PWM_LEVELS -PROGMEM const PWM_DATATYPE med_pwm_levels[] = { MED_PWM_LEVELS }; -#endif -#ifdef HIGH_PWM_LEVELS -PROGMEM const PWM_DATATYPE high_pwm_levels[] = { HIGH_PWM_LEVELS }; -#endif - -// 2 channel CCT blending ramp -#ifdef BLEND_PWM_LEVELS -// FIXME: remove this, use pwm1/2/3 instead -PROGMEM const PWM_DATATYPE blend_pwm_levels[] = { BLEND_PWM_LEVELS }; -#endif - - -// pulse frequency modulation, a.k.a. dynamic PWM -// (different ceiling / frequency at each ramp level) -// FIXME: dynamic PWM should be a per-channel option, not global -#ifdef PWM_TOPS -PROGMEM const PWM_DATATYPE pwm_tops[] = { PWM_TOPS }; -#endif - -// FIXME: jump start should be per channel / channel mode -#ifdef USE_JUMP_START - #ifndef JUMP_START_TIME - #define JUMP_START_TIME 8 // in ms, should be 4, 8, or 12 - #endif - #ifndef DEFAULT_JUMP_START_LEVEL - #define DEFAULT_JUMP_START_LEVEL 10 - #endif - #ifdef USE_CFG - #define JUMP_START_LEVEL cfg.jump_start_level - #else - #define JUMP_START_LEVEL jump_start_level - uint8_t jump_start_level = DEFAULT_JUMP_START_LEVEL; - #endif -#endif - -// RAMP_SIZE / MAX_LVL -// cfg-*.h should define RAMP_SIZE -//#define RAMP_SIZE (sizeof(stacked_pwm1_levels)/sizeof(STACKED_PWM_DATATYPE)) -#define MAX_LEVEL RAMP_SIZE - - -#endif // ifdef USE_RAMPING - diff --git a/spaghetti-monster/fsm-random.c b/spaghetti-monster/fsm-random.c deleted file mode 100644 index 91fd929..0000000 --- a/spaghetti-monster/fsm-random.c +++ /dev/null @@ -1,16 +0,0 @@ -// fsm-random.c: Random number generator for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#ifdef USE_PSEUDO_RAND -uint8_t pseudo_rand() { - static uint16_t offset = 1024; - // loop from 1024 to 4095 - offset = ((offset + 1) & 0x0fff) | 0x0400; - pseudo_rand_seed += 0b01010101; // 85 - return pgm_read_byte(offset) + pseudo_rand_seed; -} -#endif - diff --git a/spaghetti-monster/fsm-random.h b/spaghetti-monster/fsm-random.h deleted file mode 100644 index 49aa0cf..0000000 --- a/spaghetti-monster/fsm-random.h +++ /dev/null @@ -1,12 +0,0 @@ -// fsm-random.h: Random number generator for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#ifdef USE_PSEUDO_RAND -uint8_t pseudo_rand(); -// TODO: test without "volatile", in case it's not needed -volatile uint8_t pseudo_rand_seed = 0; -#endif - diff --git a/spaghetti-monster/fsm-standby.c b/spaghetti-monster/fsm-standby.c deleted file mode 100644 index 5def07c..0000000 --- a/spaghetti-monster/fsm-standby.c +++ /dev/null @@ -1,105 +0,0 @@ -// fsm-standby.c: standby mode functions for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include -#include - -#include "fsm-adc.h" -#include "fsm-wdt.h" -#include "fsm-pcint.h" - -// low-power standby mode used while off but power still connected -#define standby_mode sleep_until_eswitch_pressed -void sleep_until_eswitch_pressed() -{ - #ifdef TICK_DURING_STANDBY - WDT_slow(); - #else - WDT_off(); - #endif - - ADC_off(); - - // make sure switch isn't currently pressed - while (button_is_pressed()) {} - empty_event_sequence(); // cancel pending input on suspend - - PCINT_on(); // wake on e-switch event - - #ifdef TICK_DURING_STANDBY - // detect which type of event caused a wake-up - irq_adc = 0; - irq_wdt = 0; - irq_pcint = 0; - while (go_to_standby) { - #else - go_to_standby = 0; - #endif - - // configure sleep mode - #ifdef TICK_DURING_STANDBY - // needs a special sleep mode during measurements - if (adc_active_now) adc_sleep_mode(); - else - #endif - set_sleep_mode(SLEEP_MODE_PWR_DOWN); - - sleep_enable(); - #ifdef BODCR // only do this on MCUs which support it - sleep_bod_disable(); - #endif - sleep_cpu(); // wait here - - // something happened; wake up - sleep_disable(); - - #ifdef TICK_DURING_STANDBY - // determine what woke us up... - if (irq_pcint) { // button pressed; wake up - go_to_standby = 0; - } - if (irq_adc) { // ADC done measuring - #ifndef USE_LOWPASS_WHILE_ASLEEP - adc_reset = 1; // don't lowpass while asleep - #endif - adc_deferred_enable = 1; - adc_deferred(); - //ADC_off(); // takes care of itself - //irq_adc = 0; // takes care of itself - } - if (irq_wdt) { // generate a sleep tick - WDT_inner(); - } - } - #endif - - // don't lowpass immediately after waking - // also, reset thermal history - adc_reset = 2; - - // go back to normal running mode - // PCINT not needed any more, and can cause problems if on - // (occasional reboots on wakeup-by-button-press) - PCINT_off(); - // restore normal awake-mode interrupts - ADC_on(); - WDT_on(); -} - -#ifdef USE_IDLE_MODE -void idle_mode() -{ - // configure sleep mode - set_sleep_mode(SLEEP_MODE_IDLE); - - sleep_enable(); - sleep_cpu(); // wait here - - // something happened; wake up - sleep_disable(); -} -#endif - diff --git a/spaghetti-monster/fsm-standby.h b/spaghetti-monster/fsm-standby.h deleted file mode 100644 index 957e2e1..0000000 --- a/spaghetti-monster/fsm-standby.h +++ /dev/null @@ -1,68 +0,0 @@ -// fsm-standby.h: standby mode functions for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -// deferred "off" so we won't suspend in a weird state -// (like... during the middle of a strobe pulse) -// set this to nonzero to enter standby mode next time the system is idle -volatile uint8_t go_to_standby = 0; - -#ifdef TICK_DURING_STANDBY -#ifndef STANDBY_TICK_SPEED -#define STANDBY_TICK_SPEED 3 // every 0.128 s -/* - * From the Attiny85 manual: - * 0: 16 ms - * 1: 32 ms - * 2: 64 ms - * 3: 0.128 s - * 4: 0.256 s - * 5: 0.512 s - * 6: 1.0 s - * 7: 2.0 s - * 32: 4.0 s - * 33: 8.0 s - * (other values may have unexpected effects; not sure why the final bit is - * separated from the others, in the "32" position instead of "8", but that's - * how it is) - */ -#endif - -#if (STANDBY_TICK_SPEED == 1) -#define SLEEP_TICKS_PER_SECOND 31 -#define SLEEP_TICKS_PER_MINUTE 1800 - -#elif (STANDBY_TICK_SPEED == 2) -#define SLEEP_TICKS_PER_SECOND 16 -#define SLEEP_TICKS_PER_MINUTE 900 - -#elif (STANDBY_TICK_SPEED == 3) -#define SLEEP_TICKS_PER_SECOND 8 -#define SLEEP_TICKS_PER_MINUTE 450 - -#elif (STANDBY_TICK_SPEED == 4) -#define SLEEP_TICKS_PER_SECOND 4 -#define SLEEP_TICKS_PER_MINUTE 225 - -#elif (STANDBY_TICK_SPEED == 5) -#define SLEEP_TICKS_PER_SECOND 2 -#define SLEEP_TICKS_PER_MINUTE 113 - -#elif (STANDBY_TICK_SPEED == 6) -#define SLEEP_TICKS_PER_SECOND 1 -#define SLEEP_TICKS_PER_MINUTE 57 - -#endif -#endif - -#define standby_mode sleep_until_eswitch_pressed -void sleep_until_eswitch_pressed(); - -#ifdef USE_IDLE_MODE -// stops processing until next click or timer tick -// (I think) -void idle_mode(); -#endif - diff --git a/spaghetti-monster/fsm-states.c b/spaghetti-monster/fsm-states.c deleted file mode 100644 index 4b94ce9..0000000 --- a/spaghetti-monster/fsm-states.c +++ /dev/null @@ -1,105 +0,0 @@ -// fsm-states.c: State-handling functions for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "fsm-states.h" -#include "fsm-adc.h" - -// TODO: if callback doesn't handle current event, -// pass event to next state on stack? -// Callback return values: -// 0: event handled normally -// 1: event not handled -// 255: error (not sure what this would even mean though, or what difference it would make) -// TODO: function to call stacked callbacks until one returns "handled" - -void _set_state(StatePtr new_state, uint16_t arg, - Event exit_event, Event enter_event) { - // call old state-exit hook (don't use stack) - if (current_state != NULL) current_state(exit_event, arg); - // set new state - current_state = new_state; - // call new state-enter hook (don't use stack) - if (new_state != NULL) current_state(enter_event, arg); - - // since state changed, stop any animation in progress - interrupt_nice_delays(); -} - -int8_t push_state(StatePtr new_state, uint16_t arg) { - if (state_stack_len < STATE_STACK_SIZE) { - // TODO: call old state's exit hook? - // new hook for non-exit recursion into child? - state_stack[state_stack_len] = new_state; - state_stack_len ++; - // FIXME: use EV_stacked_state? - _set_state(new_state, arg, EV_leave_state, EV_enter_state); - return state_stack_len; - } else { - // TODO: um... how is a flashlight supposed to handle a recursion depth error? - return -1; - } -} - -StatePtr pop_state() { - // TODO: how to handle pop from empty stack? - StatePtr old_state = NULL; - StatePtr new_state = NULL; - if (state_stack_len > 0) { - state_stack_len --; - old_state = state_stack[state_stack_len]; - } - if (state_stack_len > 0) { - new_state = state_stack[state_stack_len-1]; - } - // FIXME: what should 'arg' be? (maybe re-entry should be entry with arg+1?) - _set_state(new_state, 0, EV_leave_state, EV_reenter_state); - return old_state; -} - -uint8_t set_state(StatePtr new_state, uint16_t arg) { - // FIXME: this calls exit/enter hooks it shouldn't - // (for the layer underneath the top) - pop_state(); - return push_state(new_state, arg); -} - -void set_state_deferred(StatePtr new_state, uint16_t arg) { - deferred_state = new_state; - deferred_state_arg = arg; -} - -#ifndef DONT_USE_DEFAULT_STATE -// bottom state on stack -// handles default actions for LVP, thermal regulation, etc -uint8_t default_state(Event event, uint16_t arg) { - if (0) {} // this should get compiled out - - #ifdef USE_LVP - else if (event == EV_voltage_low) { - low_voltage(); - return EVENT_HANDLED; - } - #endif - - #if 0 - #ifdef USE_THERMAL_REGULATION - else if (event == EV_temperature_high) { - high_temperature(); - return 0; - } - - else if (event == EV_temperature_low) { - low_temperature(); - return 0; - } - #endif - #endif - - // event not handled - return EVENT_NOT_HANDLED; -} -#endif - diff --git a/spaghetti-monster/fsm-states.h b/spaghetti-monster/fsm-states.h deleted file mode 100644 index 156e6cf..0000000 --- a/spaghetti-monster/fsm-states.h +++ /dev/null @@ -1,37 +0,0 @@ -// fsm-states.h: State-handling functions for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "fsm-adc.h" - -// typedefs -typedef uint8_t State(Event event, uint16_t arg); -typedef State * StatePtr; - -// top of the stack -volatile StatePtr current_state; - -// stack for states, to allow shared utility states like "input a number" -// and such, which return to the previous state after finishing -#define STATE_STACK_SIZE 8 -StatePtr state_stack[STATE_STACK_SIZE]; -uint8_t state_stack_len = 0; - -void _set_state(StatePtr new_state, uint16_t arg, - Event exit_event, Event enter_event); -int8_t push_state(StatePtr new_state, uint16_t arg); -StatePtr pop_state(); -uint8_t set_state(StatePtr new_state, uint16_t arg); - -// if loop() needs to change state, use this instead of set_state() -// (because this avoids race conditions) -volatile StatePtr deferred_state; -volatile uint16_t deferred_state_arg; -void set_state_deferred(StatePtr new_state, uint16_t arg); - -#ifndef DONT_USE_DEFAULT_STATE -uint8_t default_state(Event event, uint16_t arg); -#endif - diff --git a/spaghetti-monster/fsm-wdt.c b/spaghetti-monster/fsm-wdt.c deleted file mode 100644 index 64f006e..0000000 --- a/spaghetti-monster/fsm-wdt.c +++ /dev/null @@ -1,197 +0,0 @@ -// fsm-wdt.c: WDT (Watch Dog Timer) functions for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include -#include - -// *** Note for the AVRXMEGA3 (1-Series, eg 816 and 817), the WDT -// is not used for time-based interrupts. A new peripheral, the -// Periodic Interrupt Timer ("PIT") is used for this purpose. - -void WDT_on() -{ - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) - // interrupt every 16ms - //cli(); // Disable interrupts - wdt_reset(); // Reset the WDT - WDTCR |= (1< 0) {} // make sure the register is ready to be updated - RTC.PITCTRLA = RTC_PERIOD_CYC512_gc | RTC_PITEN_bm; // Period = 16ms, enable the PI Timer - #else - #error Unrecognized MCU type - #endif -} - -#ifdef TICK_DURING_STANDBY -inline void WDT_slow() -{ - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) - // interrupt slower - //cli(); // Disable interrupts - wdt_reset(); // Reset the WDT - WDTCR |= (1< 0) {} // make sure the register is ready to be updated - RTC.PITCTRLA = (1<<6) | (STANDBY_TICK_SPEED<<3) | RTC_PITEN_bm; // Set period, enable the PI Timer - #else - #error Unrecognized MCU type - #endif -} -#endif - -inline void WDT_off() -{ - #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) - //cli(); // Disable interrupts - wdt_reset(); // Reset the WDT - MCUSR &= ~(1< 0) {} // make sure the register is ready to be updated - RTC.PITCTRLA = 0; // Disable the PI Timer - #else - #error Unrecognized MCU type - #endif -} - -// clock tick -- this runs every 16ms (62.5 fps) -#ifdef AVRXMEGA3 // ATTINY816, 817, etc -ISR(RTC_PIT_vect) { - RTC.PITINTFLAGS = RTC_PI_bm; // clear the PIT interrupt flag -#else -ISR(WDT_vect) { -#endif - irq_wdt = 1; // WDT event happened -} - -void WDT_inner() { - irq_wdt = 0; // WDT event handled; reset flag - - static uint8_t adc_trigger = 0; - - // cache this here to reduce ROM size, because it's volatile - uint16_t ticks_since_last = ticks_since_last_event; - // increment, but loop from max back to half - ticks_since_last = (ticks_since_last + 1) \ - | (ticks_since_last & 0x8000); - // copy back to the original - ticks_since_last_event = ticks_since_last; - - // detect and emit button change events (even during standby) - uint8_t was_pressed = button_last_state; - uint8_t pressed = button_is_pressed(); - if (was_pressed != pressed) { - go_to_standby = 0; - PCINT_inner(pressed); - } - // cache again, in case the value changed - ticks_since_last = ticks_since_last_event; - - #ifdef TICK_DURING_STANDBY - // handle standby mode specially - if (go_to_standby) { - // emit a sleep tick, and process it - emit(EV_sleep_tick, ticks_since_last); - process_emissions(); - - #ifndef USE_SLEEP_LVP - return; // no sleep LVP needed if nothing drains power while off - #else - // stop here, usually... except during the first few seconds asleep, - // and once in a while afterward for sleep LVP - if ((ticks_since_last > (8 * SLEEP_TICKS_PER_SECOND)) - && (0 != (ticks_since_last & 0x0f))) return; - - adc_trigger = 0; // make sure a measurement will happen - adc_active_now = 1; // use ADC noise reduction sleep mode - ADC_on(); // enable ADC voltage measurement functions temporarily - #endif - } - else { // button handling should only happen while awake - #endif - - // if time since last event exceeds timeout, - // append timeout to current event sequence, then - // send event to current state callback - - // callback on each timer tick - if ((current_event & B_FLAGS) == (B_CLICK | B_HOLD | B_PRESS)) { - emit(EV_tick, 0); // override tick counter while holding button - } - else { - emit(EV_tick, ticks_since_last); - } - - // user held button long enough to count as a long click? - if (current_event & B_PRESS) { - // during a "hold", send a hold event each tick, with a timer - if (current_event & B_HOLD) { - emit_current_event(ticks_since_last); - } - // has button been down long enough to become a "hold"? - // (first frame of a "hold" event) - else { - if (ticks_since_last >= HOLD_TIMEOUT) { - ticks_since_last_event = 0; - current_event |= B_HOLD; - emit_current_event(0); - } - } - } - - // event in progress, but button not currently down - else if (current_event) { - // "hold" event just ended - // no timeout required when releasing a long-press - if (current_event & B_HOLD) { - //emit_current_event(ticks_since_last); // should have been emitted by PCINT_inner() - empty_event_sequence(); - } - // end and clear event after release timeout - else if (ticks_since_last >= RELEASE_TIMEOUT) { - current_event |= B_TIMEOUT; - emit_current_event(0); - empty_event_sequence(); - } - } - - #ifdef TICK_DURING_STANDBY - } - #endif - - #if defined(USE_LVP) || defined(USE_THERMAL_REGULATION) - // enable the deferred ADC handler once in a while - if (! adc_trigger) { - ADC_start_measurement(); - adc_deferred_enable = 1; - } - // timing for the ADC handler is every 32 ticks (~2Hz) - adc_trigger = (adc_trigger + 1) & 31; - #endif -} - diff --git a/spaghetti-monster/fsm-wdt.h b/spaghetti-monster/fsm-wdt.h deleted file mode 100644 index abf34c5..0000000 --- a/spaghetti-monster/fsm-wdt.h +++ /dev/null @@ -1,20 +0,0 @@ -// fsm-wdt.h: WDT (Watch Dog Timer) functions for SpaghettiMonster. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#define TICKS_PER_SECOND 62 - -void WDT_on(); -inline void WDT_off(); - -volatile uint8_t irq_wdt = 0; // WDT interrupt happened? - -#ifdef TICK_DURING_STANDBY - #if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS) - // measure battery charge while asleep - #define USE_SLEEP_LVP - #endif -#endif - diff --git a/spaghetti-monster/meteor/meteor.c b/spaghetti-monster/meteor/meteor.c deleted file mode 100644 index 9c1c000..0000000 --- a/spaghetti-monster/meteor/meteor.c +++ /dev/null @@ -1,556 +0,0 @@ -/* - * Meteor: Meteor M43 clone UI for SpaghettiMonster. - * (in progress, not really in a usable state yet) - * - * Copyright (C) 2017 Selene ToyKeeper - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "hwdef-Emisar_D4.h" -#define USE_LVP -#define USE_THERMAL_REGULATION -#define DEFAULT_THERM_CEIL 45 -#define USE_RAMPING -#define RAMP_LENGTH 150 -#define USE_BATTCHECK -#define BATTCHECK_6bars -#define DONT_DELAY_AFTER_BATTCHECK -//#define USE_EEPROM -//#define EEPROM_BYTES 5 -#include "spaghetti-monster.h" - -// FSM states -uint8_t base_off_state(Event event, uint16_t arg); -uint8_t ui1_off_state(Event event, uint16_t arg); -uint8_t ui2_off_state(Event event, uint16_t arg); -uint8_t ui3_off_state(Event event, uint16_t arg); -uint8_t base_on_state(Event event, uint16_t arg, uint8_t *mode, uint8_t *group); -uint8_t ui1_on_state(Event event, uint16_t arg); -uint8_t ui2_on_state(Event event, uint16_t arg); -uint8_t ui3_on_state(Event event, uint16_t arg); -uint8_t beacon_state(Event event, uint16_t arg); -uint8_t battcheck_state(Event event, uint16_t arg); -uint8_t strobe_state(Event event, uint16_t arg); -uint8_t biking_state(Event event, uint16_t arg); -uint8_t lockout_state(Event event, uint16_t arg); -uint8_t momentary_state(Event event, uint16_t arg); -// Not a FSM state, just handles stuff common to all low/med/hi states -uint8_t any_mode_state(Event event, uint16_t arg, uint8_t *primary, uint8_t *secondary, uint8_t *modes); - -#ifdef USE_EEPROM -void load_config(); -void save_config(); -#endif - -// fixed output levels -uint8_t levels[] = {3, 16, 30, 43, 56, 70, 83, 96, 110, 123, 137, MAX_LEVEL}; -// select an interface -uint8_t UI = 1; // 1, 2, or 3 -// UI1 -uint8_t UI1_mode = 0; -uint8_t UI1_mode1 = 1; -uint8_t UI1_mode2 = 1; -uint8_t UI1_group1[] = {0, 2}; -uint8_t UI1_group2[] = {6, 9}; -// UI2 -uint8_t UI2_mode = 0; -uint8_t UI2_mode1 = 1; -uint8_t UI2_mode2 = 0; -uint8_t UI2_mode3 = 0; -uint8_t UI2_mode4 = 0; // doesn't matter, makes other code easier -uint8_t UI2_group1[] = { 0, 2}; // moon, low -uint8_t UI2_group2[] = { 4, 6}; // mid1, mid2 -uint8_t UI2_group3[] = { 8, 10}; // high1, high2 -uint8_t UI2_group4[] = {11, 11}; // turbo only -// UI3 can access all levels, with 3 different mode memory slots -uint8_t UI3_mode = 0; -uint8_t UI3_mode1 = 2; -uint8_t UI3_mode2 = 5; -uint8_t UI3_mode3 = 8; - -#ifdef USE_THERMAL_REGULATION -// brightness before thermal step-down -uint8_t target_level = 0; -#endif - -void set_any_mode(uint8_t mode, uint8_t *group) { - set_level(levels[group[mode]]); - #ifdef USE_THERMAL_REGULATION - target_level = actual_level; - #endif -} - -void blink_fast() { - set_level(MAX_LEVEL/2); - delay_4ms(8/4); - set_level(0); -} - -uint8_t base_off_state(Event event, uint16_t arg) { - // turn emitter off when entering state - if (event == EV_enter_state) { - set_level(0); - // sleep while off (lower power use) - go_to_standby = 1; - // ensure we're in a real off state, not the base - switch(UI) { - case 1: set_state(ui1_off_state, 0); break; - case 2: set_state(ui2_off_state, 0); break; - default: set_state(ui3_off_state, 0); break; - } - return EVENT_HANDLED; - } - // 3 clicks: strobe mode - else if (event == EV_3clicks) { - set_state(beacon_state, 0); - return EVENT_HANDLED; - } - // 4 clicks: battcheck mode - else if (event == EV_4clicks) { - set_state(battcheck_state, 0); - return EVENT_HANDLED; - } - // 5 clicks: battcheck mode - else if (event == EV_5clicks) { - set_state(biking_state, 0); - return EVENT_HANDLED; - } - // 6 clicks: soft lockout mode - else if (event == EV_6clicks) { - set_state(lockout_state, 0); - return EVENT_HANDLED; - } - // 9 clicks: activate UI1 - else if (event == EV_9clicks) { - blink_fast(); - set_state(ui1_off_state, 0); - return EVENT_HANDLED; - } - // 10 clicks: activate UI2 - else if (event == EV_10clicks) { - blink_fast(); - set_state(ui2_off_state, 0); - return EVENT_HANDLED; - } - // 11 clicks: activate UI3 - else if (event == EV_11clicks) { - blink_fast(); - set_state(ui3_off_state, 0); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - -uint8_t ui1_off_state(Event event, uint16_t arg) { - UI = 1; - if (event == EV_enter_state) { - return EVENT_HANDLED; - } - // 1 click: low modes - if (event == EV_1click) { - set_any_mode(UI1_mode1, UI1_group1); - set_state(ui1_on_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: high modes - else if (event == EV_2clicks) { - set_any_mode(UI1_mode2, UI1_group2); - set_state(ui1_on_state, 1); - return EVENT_HANDLED; - } - // hold: turbo - else if (event == EV_hold) { - if (arg == 0) { - set_level(MAX_LEVEL); - } - //set_state(ui1_on_state, 3); - return EVENT_HANDLED; - } - // release hold: off - else if (event == EV_click1_hold_release) { - set_state(base_off_state, 0); - return EVENT_HANDLED; - } - return base_off_state(event, arg); -} - -uint8_t ui2_off_state(Event event, uint16_t arg) { - UI = 2; - if (event == EV_enter_state) { - return EVENT_HANDLED; - } - // 1 click: low modes - if (event == EV_1click) { - set_any_mode(UI2_mode1, UI2_group1); - set_state(ui2_on_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: high modes - else if (event == EV_2clicks) { - set_any_mode(UI2_mode3, UI2_group3); - set_state(ui2_on_state, 2); - return EVENT_HANDLED; - } - // hold: turbo - else if (event == EV_hold) { - if (arg == 0) { - set_level(MAX_LEVEL); - } - //set_state(ui1_on_state, 3); - return EVENT_HANDLED; - } - // release hold: off - else if (event == EV_click1_hold_release) { - set_state(base_off_state, 0); - return EVENT_HANDLED; - } - return base_off_state(event, arg); -} - -uint8_t ui3_off_state(Event event, uint16_t arg) { - UI = 3; - if (event == EV_enter_state) { - return EVENT_HANDLED; - } - // 1 click: memory slot 1 - if (event == EV_1click) { - set_level(levels[UI3_mode1]); - set_state(ui3_on_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: memory slot 2 - else if (event == EV_2clicks) { - set_level(levels[UI3_mode2]); - set_state(ui3_on_state, 1); - return EVENT_HANDLED; - } - // Click, hold: memory slot 3 - else if (event == EV_click2_hold) { - set_level(levels[UI3_mode3]); - set_state(ui3_on_state, 2); - return EVENT_HANDLED; - } - // hold: turbo - else if (event == EV_hold) { - if (arg == 0) { - set_level(MAX_LEVEL); - } - //set_state(ui1_on_state, 3); - return EVENT_HANDLED; - } - // release hold: off - else if (event == EV_click1_hold_release) { - set_state(base_off_state, 0); - return EVENT_HANDLED; - } - return base_off_state(event, arg); -} - -uint8_t base_on_state(Event event, uint16_t arg, uint8_t *mode, uint8_t *group) { - // 1 click: off - if (event == EV_1click) { - set_state(base_off_state, 0); - return EVENT_HANDLED; - } - #ifdef USE_THERMAL_REGULATION - // overheating: drop by an amount proportional to how far we are above the ceiling - else if (event == EV_temperature_high) { - if (actual_level > MAX_LEVEL/4) { - uint8_t stepdown = actual_level - arg; - if (stepdown < MAX_LEVEL/4) stepdown = MAX_LEVEL/4; - set_level(stepdown); - } - return EVENT_HANDLED; - } - // underheating: increase slowly if we're lower than the target - // (proportional to how low we are) - else if (event == EV_temperature_low) { - if (actual_level < target_level) { - uint8_t stepup = actual_level + (arg>>1); - if (stepup > target_level) stepup = target_level; - set_level(stepup); - } - return EVENT_HANDLED; - } - #endif - return EVENT_NOT_HANDLED; -} - -uint8_t ui1_on_state(Event event, uint16_t arg) { - // turn on LED when entering the mode - static uint8_t *mode = &UI1_mode1; - static uint8_t *group = UI1_group1; - if (event == EV_enter_state) { - UI1_mode = arg; - } - if (UI1_mode == 0) { - mode = &UI1_mode1; - group = UI1_group1; - } - else { - mode = &UI1_mode2; - group = UI1_group2; - } - - if (event == EV_enter_state) { - set_any_mode(*mode, group); - return EVENT_HANDLED; - } - // 2 clicks: toggle moon/low or mid/high - else if (event == EV_2clicks) { - *mode ^= 1; - set_any_mode(*mode, group); - return EVENT_HANDLED; - } - // hold: turbo - else if (event == EV_hold) { - if (arg == 0) set_level(MAX_LEVEL); - return EVENT_HANDLED; - } - // release: exit turbo - else if (event == EV_click1_hold_release) { - set_any_mode(*mode, group); - return EVENT_HANDLED; - } - return base_on_state(event, arg, mode, group); -} - -uint8_t ui2_on_state(Event event, uint16_t arg) { - // turn on LED when entering the mode - static uint8_t *mode = &UI2_mode1; - static uint8_t *group = UI2_group1; - if (event == EV_enter_state) { - UI2_mode = arg; - } - switch (UI2_mode) { - case 0: - mode = &UI2_mode1; - group = UI2_group1; - break; - case 1: - mode = &UI2_mode2; - group = UI2_group2; - break; - case 2: - mode = &UI2_mode3; - group = UI2_group3; - break; - default: // turbo only - mode = &UI2_mode4; - group = UI2_group4; - break; - } - - if (event == EV_enter_state) { - set_any_mode(*mode, group); - return EVENT_HANDLED; - } - // 2 clicks: toggle moon/low, mid1/mid2, or high1/high2 - else if (event == EV_2clicks) { - *mode ^= 1; - set_any_mode(*mode, group); - return EVENT_HANDLED; - } - // hold: rotate through low/mid/high/turbo - else if (event == EV_hold) { - if (arg % HOLD_TIMEOUT == 0) { - UI2_mode = (UI2_mode + 1) & 3; - } - else if (arg % HOLD_TIMEOUT == 1) { - set_any_mode(*mode, group); - } - return EVENT_HANDLED; - } - return base_on_state(event, arg, mode, group); -} - -uint8_t ui3_on_state(Event event, uint16_t arg) { - // turn on LED when entering the mode - static uint8_t *mode = &UI3_mode1; - if (event == EV_enter_state) { - UI3_mode = arg; - } - // 2 clicks: rotate through mode1/mode2/mode3 - else if (event == EV_2clicks) { - UI3_mode = (UI3_mode + 1) % 3; - } - // short click, long click: rotate through mode3/mode2/mode1 - /* - else if (event == EV_click1_hold) { - if (arg % HOLD_TIMEOUT == 0) - UI3_mode = (UI3_mode + 4) % 3; - } - */ - switch (UI3_mode) { - case 0: - mode = &UI3_mode1; - break; - case 1: - mode = &UI3_mode2; - break; - default: - mode = &UI3_mode3; - break; - } - - if ((event == EV_enter_state) || (event == EV_2clicks)) { - set_level(levels[*mode]); - return EVENT_HANDLED; - } - // short click, long click: rotate through mode3/mode2/mode1 - /* - else if (event == EV_click1_hold) { - set_level(levels[*mode]); - return EVENT_HANDLED; - } - */ - // hold: turbo - // Click, hold: ramp up - // release hold, hold again: ramp in opposite direction - return base_on_state(event, arg, mode, levels); -} - - -uint8_t blinky_base_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(base_off_state, 0); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - -uint8_t beacon_state(Event event, uint16_t arg) { - return blinky_base_state(event, arg); -} - -uint8_t battcheck_state(Event event, uint16_t arg) { - return EVENT_NOT_HANDLED; -} - -uint8_t strobe_state(Event event, uint16_t arg) { - return blinky_base_state(event, arg); -} - -uint8_t biking_state(Event event, uint16_t arg) { - return blinky_base_state(event, arg); -} - -uint8_t lockout_state(Event event, uint16_t arg) { - return blinky_base_state(event, arg); -} - -uint8_t momentary_state(Event event, uint16_t arg) { - return blinky_base_state(event, arg); -} - - -void low_voltage() { - if ((current_state == ui1_on_state) || - (current_state == ui2_on_state) || - (current_state == ui3_on_state)) { - if (actual_level > 5) { - set_level(actual_level >> 1); - } - else { - set_state(base_off_state, 0); - } - } - /* - // "step down" from blinkies to low - else if (current_state == strobe_beacon_state) { - set_state(low_mode_state, 0); - } - */ -} - -void strobe(uint8_t level, uint16_t ontime, uint16_t offtime) { - set_level(level); - if (! nice_delay_ms(ontime)) return; - set_level(0); - nice_delay_ms(offtime); -} - -#ifdef USE_EEPROM -void load_config() { - if (load_eeprom()) { - H1 = !(!(eeprom[0] & 0b00000100)); - M1 = !(!(eeprom[0] & 0b00000010)); - L1 = !(!(eeprom[0] & 0b00000001)); - H2 = eeprom[1]; - M2 = eeprom[2]; - L2 = eeprom[3]; - strobe_beacon_mode = eeprom[4]; - } -} - -void save_config() { - eeprom[0] = (H1<<2) | (M1<<1) | (L1); - eeprom[1] = H2; - eeprom[2] = M2; - eeprom[3] = L2; - eeprom[4] = strobe_beacon_mode; - - save_eeprom(); -} -#endif - -void setup() { - set_level(RAMP_SIZE/8); - delay_4ms(3); - set_level(0); - - #ifdef USE_EEPROM - load_config(); - #endif - - push_state(base_off_state, 0); -} - -void loop() { - if (0) {} - /* - if (current_state == strobe_beacon_state) { - switch(strobe_beacon_mode) { - // 0.2 Hz beacon at L1 - case 0: - strobe(low_modes[0], 500, 4500); - break; - // 0.2 Hz beacon at H1 - case 1: - strobe(hi_modes[0], 500, 4500); - break; - // 4 Hz tactical strobe at H1 - case 2: - strobe(hi_modes[0], 83, 167); - break; - // 19 Hz tactical strobe at H1 - case 3: - strobe(hi_modes[0], 17, 35); - break; - } - } - */ - - #ifdef USE_BATTCHECK - else if (current_state == battcheck_state) { - nice_delay_ms(500); // wait a moment to measure voltage - battcheck(); - set_state(base_off_state, 0); - } - #endif -} - - diff --git a/spaghetti-monster/momentary/momentary.c b/spaghetti-monster/momentary/momentary.c deleted file mode 100644 index c7a8cf1..0000000 --- a/spaghetti-monster/momentary/momentary.c +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Momentary: Very simple example UI for SpaghettiMonster. - * Is intended to be the simplest possible FSM e-switch UI. - * The light is on while the button is held; off otherwise. - * - * Copyright (C) 2017 Selene ToyKeeper - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "hwdef-Emisar_D4.h" -#define USE_LVP -#define USE_DEBUG_BLINK -#include "spaghetti-monster.h" - -volatile uint8_t brightness; -volatile uint8_t on_now; - -void light_on() { - on_now = 1; - PWM1_LVL = brightness; - PWM2_LVL = 0; -} - -void light_off() { - on_now = 0; - PWM1_LVL = 0; - PWM2_LVL = 0; -} - -uint8_t momentary_state(Event event, uint16_t arg) { - - if (event == EV_click1_press) { - brightness = 255; - light_on(); - empty_event_sequence(); // don't attempt to parse multiple clicks - return 0; - } - - else if (event == EV_release) { - light_off(); - empty_event_sequence(); // don't attempt to parse multiple clicks - go_to_standby = 1; // sleep while light is off - return 0; - } - - return 1; // event not handled -} - -// LVP / low-voltage protection -void low_voltage() { - if (brightness > 0) { - debug_blink(3); - brightness >>= 1; - if (on_now) light_on(); - } else { - debug_blink(8); - light_off(); - go_to_standby = 1; - } -} - -void setup() { - debug_blink(2); - push_state(momentary_state, 0); -} - -void loop() { } - diff --git a/spaghetti-monster/ramping-ui/ramping-ui.c b/spaghetti-monster/ramping-ui/ramping-ui.c deleted file mode 100644 index 583498a..0000000 --- a/spaghetti-monster/ramping-ui/ramping-ui.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Ramping-UI: Ramping UI for SpaghettiMonster. - * - * Copyright (C) 2017 Selene ToyKeeper - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "hwdef-Emisar_D4.h" -#define USE_LVP -#define USE_THERMAL_REGULATION -#define DEFAULT_THERM_CEIL 32 -#define USE_DELAY_MS -#define USE_DELAY_ZERO -#define USE_RAMPING -#define USE_BATTCHECK -#define BATTCHECK_VpT -#define RAMP_LENGTH 150 -#include "spaghetti-monster.h" - -// FSM states -uint8_t off_state(Event event, uint16_t arg); -uint8_t steady_state(Event event, uint16_t arg); -uint8_t strobe_state(Event event, uint16_t arg); -#ifdef USE_BATTCHECK -uint8_t battcheck_state(Event event, uint16_t arg); -uint8_t tempcheck_state(Event event, uint16_t arg); -#endif - -// brightness control -uint8_t memorized_level = MAX_1x7135; -// smooth vs discrete ramping -uint8_t ramp_step_size = 1; - -#ifdef USE_THERMAL_REGULATION -// brightness before thermal step-down -uint8_t target_level = 0; -#endif - -// strobe timing -volatile uint8_t strobe_delay = 67; -volatile uint8_t strobe_type = 0; // 0 == party strobe, 1 == tactical strobe - - -uint8_t off_state(Event event, uint16_t arg) { - // turn emitter off when entering state - if (event == EV_enter_state) { - set_level(0); - // sleep while off (lower power use) - go_to_standby = 1; - return EVENT_HANDLED; - } - // hold (initially): go to lowest level, but allow abort for regular click - else if (event == EV_click1_press) { - set_level(1); - return EVENT_HANDLED; - } - // 1 click (before timeout): go to memorized level, but allow abort for double click - else if (event == EV_click1_release) { - set_level(memorized_level); - return EVENT_HANDLED; - } - // 1 click: regular mode - else if (event == EV_1click) { - set_state(steady_state, memorized_level); - return EVENT_HANDLED; - } - // 2 clicks (initial press): off, to prep for later events - else if (event == EV_click2_press) { - set_level(0); - return EVENT_HANDLED; - } - // 2 clicks: highest mode - else if (event == EV_2clicks) { - set_state(steady_state, MAX_LEVEL); - return EVENT_HANDLED; - } - // 3 clicks: strobe mode - else if (event == EV_3clicks) { - set_state(strobe_state, 0); - return EVENT_HANDLED; - } - #ifdef USE_BATTCHECK - // 4 clicks: battcheck mode - else if (event == EV_4clicks) { - set_state(battcheck_state, 0); - return EVENT_HANDLED; - } - #endif - // hold: go to lowest level - else if (event == EV_click1_hold) { - // don't start ramping immediately; - // give the user time to release at moon level - if (arg >= HOLD_TIMEOUT) - set_state(steady_state, 1); - return EVENT_HANDLED; - } - // hold, release quickly: go to lowest level - else if (event == EV_click1_hold_release) { - set_state(steady_state, 1); - return EVENT_HANDLED; - } - // click, hold: go to highest level (for ramping down) - else if (event == EV_click2_hold) { - set_state(steady_state, MAX_LEVEL); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - - -uint8_t steady_state(Event event, uint16_t arg) { - // turn LED on when we first enter the mode - if (event == EV_enter_state) { - // remember this level, unless it's moon or turbo - if ((arg > 1) && (arg < MAX_LEVEL)) - memorized_level = arg; - // use the requested level even if not memorized - #ifdef USE_THERMAL_REGULATION - target_level = arg; - #endif - set_level(arg); - return EVENT_HANDLED; - } - // 1 click: off - else if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: go to/from highest level - else if (event == EV_2clicks) { - if (actual_level < MAX_LEVEL) { - memorized_level = actual_level; // in case we're on moon - #ifdef USE_THERMAL_REGULATION - target_level = MAX_LEVEL; - #endif - set_level(MAX_LEVEL); - } - else { - #ifdef USE_THERMAL_REGULATION - target_level = memorized_level; - #endif - set_level(memorized_level); - } - return EVENT_HANDLED; - } - // 3 clicks: go to strobe modes - else if (event == EV_3clicks) { - set_state(strobe_state, 0); - return EVENT_HANDLED; - } - // 4 clicks: toggle smooth vs discrete ramping - else if (event == EV_4clicks) { - if (ramp_step_size == 1) ramp_step_size = MAX_LEVEL/6; - else ramp_step_size = 1; - set_level(0); - delay_4ms(20/4); - set_level(memorized_level); - return EVENT_HANDLED; - } - // hold: change brightness (brighter) - else if (event == EV_click1_hold) { - // ramp slower in discrete mode - if (arg % ramp_step_size != 0) { - return EVENT_HANDLED; - } - // FIXME: make it ramp down instead, if already at max - if (actual_level + ramp_step_size < MAX_LEVEL) - memorized_level = actual_level + ramp_step_size; - else memorized_level = MAX_LEVEL; - #ifdef USE_THERMAL_REGULATION - target_level = memorized_level; - #endif - // only blink once for each threshold - if ((memorized_level != actual_level) - && ((memorized_level == MAX_1x7135) - || (memorized_level == MAX_LEVEL))) { - set_level(0); - delay_4ms(8/4); - } - set_level(memorized_level); - return EVENT_HANDLED; - } - // click, hold: change brightness (dimmer) - else if (event == EV_click2_hold) { - // ramp slower in discrete mode - if (arg % ramp_step_size != 0) { - return EVENT_HANDLED; - } - // FIXME: make it ramp up instead, if already at min - if (actual_level > ramp_step_size) - memorized_level = (actual_level-ramp_step_size); - else - memorized_level = 1; - #ifdef USE_THERMAL_REGULATION - target_level = memorized_level; - #endif - // only blink once for each threshold - if ((memorized_level != actual_level) - && ((memorized_level == MAX_1x7135) - || (memorized_level == 1))) { - set_level(0); - delay_4ms(8/4); - } - set_level(memorized_level); - return EVENT_HANDLED; - } - #ifdef USE_THERMAL_REGULATION - // TODO: test this on a real light - // overheating: drop by an amount proportional to how far we are above the ceiling - else if (event == EV_temperature_high) { - if (actual_level > MAX_LEVEL/4) { - uint8_t stepdown = actual_level - arg; - if (stepdown < MAX_LEVEL/4) stepdown = MAX_LEVEL/4; - set_level(stepdown); - } - return EVENT_HANDLED; - } - // underheating: increase slowly if we're lower than the target - // (proportional to how low we are) - else if (event == EV_temperature_low) { - if (actual_level < target_level) { - uint8_t stepup = actual_level + (arg>>1); - if (stepup > target_level) stepup = target_level; - set_level(stepup); - } - return EVENT_HANDLED; - } - #endif - return EVENT_NOT_HANDLED; -} - - -uint8_t strobe_state(Event event, uint16_t arg) { - if (event == EV_enter_state) { - return EVENT_HANDLED; - } - // 1 click: off - else if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: toggle party strobe vs tactical strobe - else if (event == EV_2clicks) { - strobe_type ^= 1; - return EVENT_HANDLED; - } - // 3 clicks: go back to regular modes - else if (event == EV_3clicks) { - set_state(steady_state, memorized_level); - return EVENT_HANDLED; - } - // hold: change speed (go faster) - else if (event == EV_click1_hold) { - if ((arg & 1) == 0) { - if (strobe_delay > 8) strobe_delay --; - } - return EVENT_HANDLED; - } - // click, hold: change speed (go slower) - else if (event == EV_click2_hold) { - if ((arg & 1) == 0) { - if (strobe_delay < 255) strobe_delay ++; - } - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - - -#ifdef USE_BATTCHECK -uint8_t battcheck_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: tempcheck mode - else if (event == EV_2clicks) { - set_state(tempcheck_state, 0); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - -uint8_t tempcheck_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} -#endif - - -void low_voltage() { - // "step down" from strobe to something low - if (current_state == strobe_state) { - set_state(steady_state, RAMP_SIZE/6); - } - // in normal mode, step down by half or turn off - else if (current_state == steady_state) { - if (actual_level > 1) { - set_level(actual_level >> 1); - } - else { - set_state(off_state, 0); - } - } - // all other modes, just turn off when voltage is low - else { - set_state(off_state, 0); - } -} - - -void setup() { - set_level(RAMP_SIZE/8); - delay_4ms(3); - set_level(0); - - push_state(off_state, 0); -} - - -void loop() { - if (current_state == strobe_state) { - set_level(MAX_LEVEL); - if (strobe_type == 0) { // party strobe - if (strobe_delay < 30) delay_zero(); - else delay_ms(1); - } else { //tactical strobe - nice_delay_ms(strobe_delay >> 1); - } - set_level(0); - nice_delay_ms(strobe_delay); - } - #ifdef USE_BATTCHECK - else if (current_state == battcheck_state) { - battcheck(); - } - else if (current_state == tempcheck_state) { - blink_num(temperature); - nice_delay_ms(1000); - } - #endif -} diff --git a/spaghetti-monster/rampingios/Makefile b/spaghetti-monster/rampingios/Makefile deleted file mode 100644 index 8db198e..0000000 --- a/spaghetti-monster/rampingios/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -all: - ./build-all.sh - -clean: - rm -f *.hex cfg-*.h *~ *.elf *.o - -.phony: clean diff --git a/spaghetti-monster/rampingios/build-all.sh b/spaghetti-monster/rampingios/build-all.sh deleted file mode 100755 index 106dc15..0000000 --- a/spaghetti-monster/rampingios/build-all.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -cp -av ../anduril/cfg-emisar*.h . - -UI=rampingiosv3 - -for TARGET in cfg-*.h ; do - NAME=$(echo "$TARGET" | perl -ne '/cfg-(.*).h/ && print "$1\n";') - echo "===== $NAME =====" - echo ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}" - ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}" - mv -f "$UI".hex "$UI".$NAME.hex -done diff --git a/spaghetti-monster/rampingios/rampingios-v3.html b/spaghetti-monster/rampingios/rampingios-v3.html deleted file mode 100644 index f72d1ec..0000000 --- a/spaghetti-monster/rampingios/rampingios-v3.html +++ /dev/null @@ -1,501 +0,0 @@ - - - - - - - RampingIOS V3 Manual :: Phil! Gold - - - - - - - - - - - - - - - - - - - -
-

Tue, 28 Aug 2018

- -
-

RampingIOS V3 Manual

- -
- -

- - - - -
RampingIOS V3 UI diagram
-

- -

The Emisar D4S flashlights use a firmware named RampingIOS -V3. (The Emisar D4, D1, and D1S -all use RampingIOS V2.) There's not really a manual; the -only thing we get is the diagram on the right. It's reasonably -comprehensive, but there's a fair amount of detail it merely summarizes, -so I thought a textual manual would be nice.

- -

The Emisar D4S only works when the head and tailcap are tightened fully. -You can physically lock it out--prevent it from turning on -accidentally--by simply loosening the tailcap a small amount. A quarter -turn will do it.

- -

Emisar lights are known for their ramping interfaces. Rather than have a -small number of distinct brightness levels, they can vary their brightness -anywhere between their lowest and highest levels, like a light on a -dimmer. The D4S is in ramping mode by default, but it also has a stepped -mode that can be configured to be closer to how non-ramping lights work.

- -

Each mode--ramping and stepped--can have differently-configured brightness -floors and ceilings.

- -

The driver for the D4S has two different chipsets. At low brightness -levels, a fairly-efficient but low-power chipset (called a 7135) is -used. These lowest brightness levels are called the "regulated levels". -Each regulated level will always be the same brightness regardless of how -much charge the battery has. Above a particular brightness level, the -light switches over to a less-efficient but high-power chipset (called a -FET). These levels are called "direct-drive". The brightness of the -direct-drive levels is directly related to the battery's charge level; the -more charged the battery, the brighter the levels. The light is at its -most efficient, in terms of power used for every lumen generated, at the -brightest regulated level. When the light is first powered by tightening -the tailcap, it will default to this level.

- -

At higher brightness levels, the light's LEDs generate a lot of heat. If -the light exceeds its configured maximum temperature, it will begin -dimming itself automatically until the temperature drops below the allowed -maximum.

- -

The D4S has a set of cyan-colored auxiliary LEDs that can be on when the -main LEDs are off. You can configure the behavior of the aux LEDs.

- -

Basic Usage

- -

The default mode for the light is ramping mode. Triple-pressing the -button (3 clicks) while the light is on will toggle between ramping -and stepped mode.

- -

While the light is off, press and release the button (1 click) to turn -it on. It will turn on at the last-used brightness level. (This is -called "mode memory".) Immediately after loosening and tightening the -tailcap (or after changing the battery), the memorized level will be the -light's max regulated level.

- -

When the light is on, 1 click will turn it off. The current brightness -level will be memorized for future use. There's a fraction of a second -delay between pressing the button and the light actually turning off. -That's because of the way the light processes input; it's waiting to make -sure you're only going to press the button once (since multiple presses -will trigger other actions).

- -

When the light is on, holding the button down will brighten the light. In -ramping mode, the brightness will increase gradually ("ramping up"). In -stepped mode, the light will jump through increasing brightness levels. -If you press, release, and then hold the button, it will begin dimming. -In ramping mode, the brightness will decrease gradually ("ramping -down"). In stepped mode, the light will jump through decreasing -brightness levels. While the light is changing, if you release the button -and immediately hold it again, the direction (dimming or brightening) will -switch.

- -

In ramping mode, while the light is ramping, it'll briefly blink off and -on again at two different brightness levels: the maximum regulated level -and the brightness ceiling.

- -

While the light is off, double-pressing the button (2 clicks) will -immediately jump to the brightness ceiling.

- -

While the light is on, 2 clicks will jump to the maximum brightness -level, regardless of the configured brightness ceiling. Another two -clicks will go back to the previous brightness level.

- -

While the light is off, if you hold the button the light will turn on at -its lowest level. If you continue holding the button, the light will -begin brightening from there.

- -
Configuration Menus
- -

The light has several different configuration modes. Each of those modes -works more or less the same way. The mode will have a series of menu -items that it will go through. For each menu item, the light will first -blink a number of times corresponding to the item number (first, second, -etc.) After that, the light will begin fluttering on and off fairly -quickly. While the light is fluttering, you can click the button a number -of times; the light will count the number of button presses and use that -number as its new configuration for that menu item. After a short period -of time, the fluttering will stop and the light will move on to the next -menu item. After the light has gone through all of the menu items, it -will return to whatever mode it was in before entering the configuration -mode.

- -

If you don't press the button during a particular menu item's fluttering, -that item will remain unchanged.

- -
Configuring the Basic Modes
- -

While the light is on, 4 clicks will enter ramping or stepped -configuration mode, depending on which mode the light was in before the 4 -clicks.

- -

For ramping mode, there are two menu options:

- -
    -
  1. Brightness floor (default 1/150)
  2. -
  3. Brightness ceiling (default 150/150)
  4. -
- -

During the floor configuration, press the button equal to the number of -ramping levels (out of 150) at which the floor should be. To set the -lowest possible floor, click the button once.

- -

The ceiling is configured similarly, but you press the button equal to the -number of steps away from maximum brightness. To set the highest possible -ceiling (at max brightness), click the button once.

- -

For stepped mode, there are three menu options:

- -
    -
  1. Brightness floor (default 20/150)
  2. -
  3. Brightness ceiling (default 120/150)
  4. -
  5. Number of steps (default 7)
  6. -
- -

Other Modes

- -

The other modes largely involve multiple clicks from off. Most of them -are not generally needed for everyday use, but they supplement the light's -basic operations.

- -
BattCheck/TempCheck Modes
- -

From off, 3 clicks will enter "BattCheck" mode, which blinks out the -current battery voltage. First it blinks the number of volts, then it -pauses, then it blinks out the tenths of volts. Thus, if the battery were -at 3.5 volts, the light would blink three times, pause, then five times. -For zeroes, it gives a very short blink.

- -

A fully-charged lithium-ion battery is 4.2 volts. The light considers 2.8 -volts to be an empty battery and won't turn on if the battery is at or -below 2.8 volts.

- -

The voltage sequence will continue blinking until you turn off the light -with a single click.

- -

While the light is in BattCheck mode, 2 clicks will enter TempCheck -mode. Instead of blinking out the battery voltage, the light will start -blinking out its current temperature in degrees Celsius, first the tens -digit then the units digit. Like BattCheck mode, the light will continue -blinking out the temperature until you turn it off with a single click.

- -

While the light is in TempCheck mode, 4 clicks will enter thermal -configuration mode. See the thermal configuration mode documentation -below for how that works.

- -
Tactical Mode
- -

From off, 4 clicks will enter "tactical" or "momentary" mode. The -light will flash once to show that it's entered the mode. The auxiliary -LEDs will turn off (if they were on). In tactical mode, the light will -turn on at its memorized brightness for as long as the button is being -held down. It will turn off as soon as the button is released.

- -

There's no button press combination that will exit tactical mode. To exit -it, you will have to partially unscrew and retighten the tailcap.

- -
Lockout Mode
- -

From off, 6 clicks will enter lockout mode. The light will flash -twice to show that it's entered the mode. There's a separate aux LED mode -for lockout mode, so you can tell whether the light is in lockout or not.

- -

In lockout mode, pressing the button will turn on the light at its lowest -brightness ("moonlight mode") for as long as the button is held down.

- -

Another 6 clicks will exit lockout mode. The light will flash twice to -show that it's left the mode.

- -

While in lockout mode, 3 clicks will cycle through the various -settings for the aux LEDs in lockout mode. The four modes are, in order: -low, high, blink (on high), and off. The default mode is blink.

- -

Remember that loosening the tailcap a quarter turn will also lock out the -light. Using the 6 clicks is called "electronic lockout", while turning -the tailcap is "physical lockout".

- -
Aux LED Configuration
- -

From off, 7 clicks will cycle to the next aux LED mode. The four -modes are, in order: low, high, blink (on high), and off. The default -mode is low.

- -
Beacon Mode
- -

From off, 8 clicks will enter beacon mode. In beacon mode, the light -will blink on and off every few seconds.

- -

By default, the light will blink every two seconds. To change the timing, -use 4 clicks while in beacon mode. The light will enter a one-item -menu. During the flickering for input, press the button a number of times -equal to the number of seconds between blinks.

- -

1 click will exit beacon mode.

- -
Thermal Configuration Mode
- -

From off, 10 clicks will enter thermal configuration mode.

- -

The menu items here are:

- -
    -
  1. Current temperature (every click is one degree Celsius)
  2. -
  3. Temperature ceiling (every click is one degree above 30°C)
  4. -
- -

The "current temperature" item can be used to adjust the calibration of -the light's temperature sensor. To use it, make sure the light has been -off long enough that all of its components have cooled (or warmed) to the -ambient temperature. Check the ambient temperature using a thermometer -you trust. Go to thermal configuration mode, and enter the current -temperature by clicking the button a number of times equal to the -temperature in degrees Celsius. (If it's 22°C, click the button 22 -times.)

- -

You can check the default calibration by entering TempCheck mode from a -room-temperature light. The D4Ss are supposed to go through a temperature -calibration at the factory, so hopefully most of them won't need manual -thermal calibration.

- -

The temperature ceiling is simply the highest temperature the light should -be allowed to reach. Once it hits its temperature ceiling, it will -progressively dim itself until the temperature stabilizes below the -ceiling. Note that the number of clicks in that menu option is added to -30 to reach the actual ceiling. (Thus, you can't set a ceiling below -31°C.) The maximum allowed ceiling is 70°C.

- -

The default temperature ceiling is 45°C.

- - -
- - - -

-
Phil! Gold
- - - - diff --git a/spaghetti-monster/rampingios/rampingios-v3.md b/spaghetti-monster/rampingios/rampingios-v3.md deleted file mode 100644 index bc0e2b7..0000000 --- a/spaghetti-monster/rampingios/rampingios-v3.md +++ /dev/null @@ -1,262 +0,0 @@ -RampingIOS V3 Manual - -This Markdown-formatted manual was contributed by phil_g under a -Creative Commons CC0 waiver: - http://aperiodic.net/phil/archives/Geekery/rampingios-v3.html - https://creativecommons.org/publicdomain/zero/1.0/ - - -
- - - - -
RampingIOS V3 UI diagram
-
- -The Emisar [D4S][emisar-d4s] flashlights use a firmware named RampingIOS -V3. (The Emisar [D4][emisar-d4], [D1][emisar-d1], and [D1S][emisar-d1s] -all use [RampingIOS V2][rampingios-v2].) There's not really a manual; the -only thing we get is the diagram on the right. It's reasonably -comprehensive, but there's a fair amount of detail it merely summarizes, -so I thought a textual manual would be nice. - - [emisar-d4]: https://intl-outdoor.com/emisar-d4-high-power-led-flashlight-p-921.html - [emisar-d1]: https://intl-outdoor.com/emisar-d1-mini-thrower-p-922.html - [emisar-d1s]: https://intl-outdoor.com/emisar-d1s-thrower-p-926.html - [emisar-d4s]: https://intl-outdoor.com/emisar-d4s-26650-high-power-led-flashlight-p-932.html - [rampingios-v2]: http://aperiodic.net/phil/archives/Geekery/rampingios-v2.html - -The Emisar D4S only works when the head and tailcap are tightened fully. -You can physically lock it out--prevent it from turning on -accidentally--by simply loosening the tailcap a small amount. A quarter -turn will do it. - -Emisar lights are known for their ramping interfaces. Rather than have a -small number of distinct brightness levels, they can vary their brightness -anywhere between their lowest and highest levels, like a light on a -dimmer. The D4S is in ramping mode by default, but it also has a stepped -mode that can be configured to be closer to how non-ramping lights work. - -Each mode--ramping and stepped--can have differently-configured brightness -floors and ceilings. - -The driver for the D4S has two different chipsets. At low brightness -levels, a fairly-efficient but low-power chipset (called a *7135*) is -used. These lowest brightness levels are called the "*regulated levels*". -Each regulated level will always be the same brightness regardless of how -much charge the battery has. Above a particular brightness level, the -light switches over to a less-efficient but high-power chipset (called a -*FET*). These levels are called "*direct-drive*". The brightness of the -direct-drive levels is directly related to the battery's charge level; the -more charged the battery, the brighter the levels. The light is at its -most efficient, in terms of power used for every lumen generated, at the -brightest regulated level. When the light is first powered by tightening -the tailcap, it will default to this level. - -At higher brightness levels, the light's LEDs generate a lot of heat. If -the light exceeds its configured maximum temperature, it will begin -dimming itself automatically until the temperature drops below the allowed -maximum. - -The D4S has a set of cyan-colored auxiliary LEDs that can be on when the -main LEDs are off. You can configure the behavior of the aux LEDs. - -#### Basic Usage - -The default mode for the light is ramping mode. Triple-pressing the -button (**3 clicks**) while the light is on will toggle between ramping -and stepped mode. - -While the light is off, press and release the button (**1 click**) to turn -it on. It will turn on at the last-used brightness level. (This is -called "*mode memory*".) Immediately after loosening and tightening the -tailcap (or after changing the battery), the memorized level will be the -light's max regulated level. - -When the light is on, 1 click will turn it off. The current brightness -level will be memorized for future use. There's a fraction of a second -delay between pressing the button and the light actually turning off. -That's because of the way the light processes input; it's waiting to make -sure you're only going to press the button once (since multiple presses -will trigger other actions). - -When the light is on, holding the button down will brighten the light. In -ramping mode, the brightness will increase gradually ("*ramping up*"). In -stepped mode, the light will jump through increasing brightness levels. -If you press, release, and then hold the button, it will begin dimming. -In ramping mode, the brightness will decrease gradually ("*ramping -down*"). In stepped mode, the light will jump through decreasing -brightness levels. While the light is changing, if you release the button -and immediately hold it again, the direction (dimming or brightening) will -switch. - -In ramping mode, while the light is ramping, it'll briefly blink off and -on again at two different brightness levels: the maximum regulated level -and the brightness ceiling. - -While the light is off, double-pressing the button (**2 clicks**) will -immediately jump to the brightness ceiling. - -While the light is on, **2 clicks** will jump to the maximum brightness -level, regardless of the configured brightness ceiling. Another two -clicks will go back to the previous brightness level. - -While the light is off, if you hold the button the light will turn on at -its lowest level. If you continue holding the button, the light will -begin brightening from there. - -##### Configuration Menus - -The light has several different configuration modes. Each of those modes -works more or less the same way. The mode will have a series of menu -items that it will go through. For each menu item, the light will first -blink a number of times corresponding to the item number (first, second, -etc.) After that, the light will begin fluttering on and off fairly -quickly. While the light is fluttering, you can click the button a number -of times; the light will count the number of button presses and use that -number as its new configuration for that menu item. After a short period -of time, the fluttering will stop and the light will move on to the next -menu item. After the light has gone through all of the menu items, it -will return to whatever mode it was in before entering the configuration -mode. - -If you don't press the button during a particular menu item's fluttering, -that item will remain unchanged. - -##### Configuring the Basic Modes - -While the light is on, **4 clicks** will enter ramping or stepped -configuration mode, depending on which mode the light was in before the 4 -clicks. - -For ramping mode, there are two menu options: - - 1. Brightness floor (default 1/150) - 2. Brightness ceiling (default 150/150) - -During the floor configuration, press the button equal to the number of -ramping levels (out of 150) at which the floor should be. To set the -lowest possible floor, click the button once. - -The ceiling is configured similarly, but you press the button equal to the -number of steps away from maximum brightness. To set the highest possible -ceiling (at max brightness), click the button once. - -For stepped mode, there are three menu options: - - 1. Brightness floor (default 20/150) - 2. Brightness ceiling (default 120/150) - 3. Number of steps (default 7) - -#### Other Modes - -The other modes largely involve multiple clicks from off. Most of them -are not generally needed for everyday use, but they supplement the light's -basic operations. - -##### BattCheck/TempCheck Modes - -From off, **3 clicks** will enter "BattCheck" mode, which blinks out the -current battery voltage. First it blinks the number of volts, then it -pauses, then it blinks out the tenths of volts. Thus, if the battery were -at 3.5 volts, the light would blink three times, pause, then five times. -For zeroes, it gives a very short blink. - -A fully-charged lithium-ion battery is 4.2 volts. The light considers 2.8 -volts to be an empty battery and won't turn on if the battery is at or -below 2.8 volts. - -The voltage sequence will continue blinking until you turn off the light -with a single click. - -While the light is in BattCheck mode, **2 clicks** will enter TempCheck -mode. Instead of blinking out the battery voltage, the light will start -blinking out its current temperature in degrees Celsius, first the tens -digit then the units digit. Like BattCheck mode, the light will continue -blinking out the temperature until you turn it off with a single click. - -While the light is in TempCheck mode, **4 clicks** will enter thermal -configuration mode. See the thermal configuration mode documentation -below for how that works. - -##### Tactical Mode - -From off, **4 clicks** will enter "tactical" or "momentary" mode. The -light will flash once to show that it's entered the mode. The auxiliary -LEDs will turn off (if they were on). In tactical mode, the light will -turn on at its memorized brightness for as long as the button is being -held down. It will turn off as soon as the button is released. - -There's no button press combination that will exit tactical mode. To exit -it, you will have to partially unscrew and retighten the tailcap. - -##### Lockout Mode - -From off, **6 clicks** will enter lockout mode. The light will flash -twice to show that it's entered the mode. There's a separate aux LED mode -for lockout mode, so you can tell whether the light is in lockout or not. - -In lockout mode, pressing the button will turn on the light at its lowest -brightness ("*moonlight mode*") for as long as the button is held down. - -Another 6 clicks will exit lockout mode. The light will flash twice to -show that it's left the mode. - -While in lockout mode, **3 clicks** will cycle through the various -settings for the aux LEDs in lockout mode. The four modes are, in order: -low, high, blink (on high), and off. The default mode is blink. - -Remember that loosening the tailcap a quarter turn will also lock out the -light. Using the 6 clicks is called "*electronic lockout*", while turning -the tailcap is "*physical lockout*". - -##### Aux LED Configuration - -From off, **7 clicks** will cycle to the next aux LED mode. The four -modes are, in order: low, high, blink (on high), and off. The default -mode is low. - -##### Beacon Mode - -From off, **8 clicks** will enter beacon mode. In beacon mode, the light -will blink on and off every few seconds. - -By default, the light will blink every two seconds. To change the timing, -use **4 clicks** while in beacon mode. The light will enter a one-item -menu. During the flickering for input, press the button a number of times -equal to the number of seconds between blinks. - -1 click will exit beacon mode. - -##### Thermal Configuration Mode - -From off, **10 clicks** will enter thermal configuration mode. - -The menu items here are: - - 1. Current temperature (every click is one degree Celsius) - 2. Temperature ceiling (every click is one degree *above 30°C*) - -The "current temperature" item can be used to adjust the calibration of -the light's temperature sensor. To use it, make sure the light has been -off long enough that all of its components have cooled (or warmed) to the -ambient temperature. Check the ambient temperature using a thermometer -you trust. Go to thermal configuration mode, and enter the current -temperature by clicking the button a number of times equal to the -temperature in degrees Celsius. (If it's 22°C, click the button 22 -times.) - -You can check the default calibration by entering TempCheck mode from a -room-temperature light. The D4Ss are supposed to go through a temperature -calibration at the factory, so hopefully most of them won't need manual -thermal calibration. - -The temperature ceiling is simply the highest temperature the light should -be allowed to reach. Once it hits its temperature ceiling, it will -progressively dim itself until the temperature stabilizes below the -ceiling. Note that the number of clicks in that menu option is added to -*30* to reach the actual ceiling. (Thus, you can't set a ceiling below -31°C.) The maximum allowed ceiling is 70°C. - -The default temperature ceiling is 45°C. diff --git a/spaghetti-monster/rampingios/rampingios-v3.txt b/spaghetti-monster/rampingios/rampingios-v3.txt deleted file mode 100644 index 4598a76..0000000 --- a/spaghetti-monster/rampingios/rampingios-v3.txt +++ /dev/null @@ -1,324 +0,0 @@ -RampingIOS V3 Manual -http://aperiodic.net/phil/archives/Geekery/rampingios-v3.html - -Tue, 28 Aug 2018 -9:47AM | Geekery | # - - -RampingIOS V3 Manual --------------------- - -[rampingiosv3-ui.png] RampingIOS V3 UI diagram - -The Emisar D4S flashlights use a firmware named RampingIOS V3. (The Emisar D4, -D1, and D1S all use RampingIOS V2.) There's not really a manual; the only thing -we get is the diagram on the right. It's reasonably comprehensive, but there's -a fair amount of detail it merely summarizes, so I thought a textual manual -would be nice. - -The Emisar D4S only works when the head and tailcap are tightened fully. You -can physically lock it out--prevent it from turning on accidentally--by simply -loosening the tailcap a small amount. A quarter turn will do it. - -Emisar lights are known for their ramping interfaces. Rather than have a small -number of distinct brightness levels, they can vary their brightness anywhere -between their lowest and highest levels, like a light on a dimmer. The D4S is -in ramping mode by default, but it also has a stepped mode that can be -configured to be closer to how non-ramping lights work. - -Each mode--ramping and stepped--can have differently-configured brightness -floors and ceilings. - -The driver for the D4S has two different chipsets. At low brightness levels, a -fairly-efficient but low-power chipset (called a 7135) is used. These lowest -brightness levels are called the "regulated levels". Each regulated level will -always be the same brightness regardless of how much charge the battery has. -Above a particular brightness level, the light switches over to a -less-efficient but high-power chipset (called a FET). These levels are called " -direct-drive". The brightness of the direct-drive levels is directly related to -the battery's charge level; the more charged the battery, the brighter the -levels. The light is at its most efficient, in terms of power used for every -lumen generated, at the brightest regulated level. When the light is first -powered by tightening the tailcap, it will default to this level. - -At higher brightness levels, the light's LEDs generate a lot of heat. If the -light exceeds its configured maximum temperature, it will begin dimming itself -automatically until the temperature drops below the allowed maximum. - -The D4S has a set of cyan-colored auxiliary LEDs that can be on when the main -LEDs are off. You can configure the behavior of the aux LEDs. - - -Basic Usage ------------ - -The default mode for the light is ramping mode. Triple-pressing the button (3 -clicks) while the light is on will toggle between ramping and stepped mode. - -While the light is off, press and release the button (1 click) to turn it on. -It will turn on at the last-used brightness level. (This is called "mode memory -".) Immediately after loosening and tightening the tailcap (or after changing -the battery), the memorized level will be the light's max regulated level. - -When the light is on, 1 click will turn it off. The current brightness level -will be memorized for future use. There's a fraction of a second delay between -pressing the button and the light actually turning off. That's because of the -way the light processes input; it's waiting to make sure you're only going to -press the button once (since multiple presses will trigger other actions). - -When the light is on, holding the button down will brighten the light. In -ramping mode, the brightness will increase gradually ("ramping up"). In stepped -mode, the light will jump through increasing brightness levels. If you press, -release, and then hold the button, it will begin dimming. In ramping mode, the -brightness will decrease gradually ("ramping down"). In stepped mode, the light -will jump through decreasing brightness levels. While the light is changing, if -you release the button and immediately hold it again, the direction (dimming or -brightening) will switch. - -In ramping mode, while the light is ramping, it'll briefly blink off and on -again at two different brightness levels: the maximum regulated level and the -brightness ceiling. - -While the light is off, double-pressing the button (2 clicks) will immediately -jump to the brightness ceiling. - -While the light is on, 2 clicks will jump to the maximum brightness level, -regardless of the configured brightness ceiling. Another two clicks will go -back to the previous brightness level. - -While the light is off, if you hold the button the light will turn on at its -lowest level. If you continue holding the button, the light will begin -brightening from there. - - -Configuration Menus -------------------- - -The light has several different configuration modes. Each of those modes works -more or less the same way. The mode will have a series of menu items that it -will go through. For each menu item, the light will first blink a number of -times corresponding to the item number (first, second, etc.) After that, the -light will begin fluttering on and off fairly quickly. While the light is -fluttering, you can click the button a number of times; the light will count -the number of button presses and use that number as its new configuration for -that menu item. After a short period of time, the fluttering will stop and the -light will move on to the next menu item. After the light has gone through all -of the menu items, it will return to whatever mode it was in before entering -the configuration mode. - -If you don't press the button during a particular menu item's fluttering, that -item will remain unchanged. - - -Configuring the Basic Modes - -While the light is on, 4 clicks will enter ramping or stepped configuration -mode, depending on which mode the light was in before the 4 clicks. - -For ramping mode, there are two menu options: - - 1. Brightness floor (default 1/150) - 2. Brightness ceiling (default 150/150) - -During the floor configuration, press the button equal to the number of ramping -levels (out of 150) at which the floor should be. To set the lowest possible -floor, click the button once. - -The ceiling is configured similarly, but you press the button equal to the -number of steps away from maximum brightness. To set the highest possible -ceiling (at max brightness), click the button once. - -For stepped mode, there are three menu options: - - 1. Brightness floor (default 20/150) - 2. Brightness ceiling (default 120/150) - 3. Number of steps (default 7) - - -Other Modes ------------ - -The other modes largely involve multiple clicks from off. Most of them are not -generally needed for everyday use, but they supplement the light's basic -operations. - - -BattCheck/TempCheck Modes - -From off, 3 clicks will enter "BattCheck" mode, which blinks out the current -battery voltage. First it blinks the number of volts, then it pauses, then it -blinks out the tenths of volts. Thus, if the battery were at 3.5 volts, the -light would blink three times, pause, then five times. For zeroes, it gives a -very short blink. - -A fully-charged lithium-ion battery is 4.2 volts. The light considers 2.8 volts -to be an empty battery and won't turn on if the battery is at or below 2.8 -volts. - -The voltage sequence will continue blinking until you turn off the light with a -single click. - -While the light is in BattCheck mode, 2 clicks will enter TempCheck mode. -Instead of blinking out the battery voltage, the light will start blinking out -its current temperature in degrees Celsius, first the tens digit then the units -digit. Like BattCheck mode, the light will continue blinking out the -temperature until you turn it off with a single click. - -While the light is in TempCheck mode, 4 clicks will enter thermal configuration -mode. See the thermal configuration mode documentation below for how that -works. - - -Tactical Mode - -From off, 4 clicks will enter "tactical" or "momentary" mode. The light will -flash once to show that it's entered the mode. The auxiliary LEDs will turn off -(if they were on). In tactical mode, the light will turn on at its memorized -brightness for as long as the button is being held down. It will turn off as -soon as the button is released. - -There's no button press combination that will exit tactical mode. To exit it, -you will have to partially unscrew and retighten the tailcap. - - -Lockout Mode - -From off, 6 clicks will enter lockout mode. The light will flash twice to show -that it's entered the mode. There's a separate aux LED mode for lockout mode, -so you can tell whether the light is in lockout or not. - -In lockout mode, pressing the button will turn on the light at its lowest -brightness ("moonlight mode") for as long as the button is held down. - -Another 6 clicks will exit lockout mode. The light will flash twice to show -that it's left the mode. - -While in lockout mode, 3 clicks will cycle through the various settings for the -aux LEDs in lockout mode. The four modes are, in order: low, high, blink (on -high), and off. The default mode is blink. - -Remember that loosening the tailcap a quarter turn will also lock out the -light. Using the 6 clicks is called "electronic lockout", while turning the -tailcap is "physical lockout". - - -Aux LED Configuration - -From off, 7 clicks will cycle to the next aux LED mode. The four modes are, in -order: low, high, blink (on high), and off. The default mode is low. - - -Beacon Mode - -From off, 8 clicks will enter beacon mode. In beacon mode, the light will blink -on and off every few seconds. - -By default, the light will blink every two seconds. To change the timing, use 4 -clicks while in beacon mode. The light will enter a one-item menu. During the -flickering for input, press the button a number of times equal to the number of -seconds between blinks. - -1 click will exit beacon mode. - - -Thermal Configuration Mode - -From off, 10 clicks will enter thermal configuration mode. - -The menu items here are: - - 1. Current temperature (every click is one degree Celsius) - 2. Temperature ceiling (every click is one degree above 30?C) - -The "current temperature" item can be used to adjust the calibration of the -light's temperature sensor. To use it, make sure the light has been off long -enough that all of its components have cooled (or warmed) to the ambient -temperature. Check the ambient temperature using a thermometer you trust. Go to -thermal configuration mode, and enter the current temperature by clicking the -button a number of times equal to the temperature in degrees Celsius. (If it's -22?C, click the button 22 times.) - -You can check the default calibration by entering TempCheck mode from a -room-temperature light. The D4Ss are supposed to go through a temperature -calibration at the factory, so hopefully most of them won't need manual thermal -calibration. - -The temperature ceiling is simply the highest temperature the light should be -allowed to reach. Once it hits its temperature ceiling, it will progressively -dim itself until the temperature stabilizes below the ceiling. Note that the -number of clicks in that menu option is added to 30 to reach the actual -ceiling. (Thus, you can't set a ceiling below 31?C.) The maximum allowed -ceiling is 70?C. - -The default temperature ceiling is 45?C. - - -Static - - * zsh prompt - * PGP - * SSH - * MTA - * tutorials - * config files - * desktop - * books I own - * stuff I'm giving away - * Dr. Who eps I have - * bookmarks - * photos - * about - -Directory - - * Root (143) - + Books (32) - + Events (7) - o Burning Man (3) - o Camping (2) - o PDF (2) - + Geekery (36) - o Test (3) - + General (24) - + Links (12) - o Slashdot (1) - + MTA (22) - + Recipes (4) - + Video Games (6) - o FFXI (1) - -Archive - - ?August? -Sun Mon Tue Wed Thu Fri Sat - 1 2 3 4 -5 6 7 8 9 10 11 -12 13 14 15 16 17 18 -19 20 21 22 23 24 25 -26 27 28 29 30 31 - - ?2018? - Months -Jan Feb Mar Apr May Jun -Jul Aug Sep Oct Nov Dec - -Search - -[ ] -Powered by Google - -Currently Reading - -Recent Books - -------------------------------------------------------------------------------- - -Copyright (C) 2018 Phil Gold - -Back to main page. - - * Valid HTML 4.01 - * Valid CSS 2 - * RSS syndication - * Valid RSS 1.0 - diff --git a/spaghetti-monster/rampingios/rampingiosv3-ui.png b/spaghetti-monster/rampingios/rampingiosv3-ui.png deleted file mode 100644 index d02dbf6..0000000 Binary files a/spaghetti-monster/rampingios/rampingiosv3-ui.png and /dev/null differ diff --git a/spaghetti-monster/rampingios/rampingiosv3.c b/spaghetti-monster/rampingios/rampingiosv3.c deleted file mode 100644 index e990a5a..0000000 --- a/spaghetti-monster/rampingios/rampingiosv3.c +++ /dev/null @@ -1,1253 +0,0 @@ -/* - * RampingIOS V3: FSM-based version of RampingIOS V2 UI, with upgrades. - * - * Copyright (C) 2018-2019 Selene ToyKeeper - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/********* User-configurable options *********/ -// Anduril config file name (set it here or define it at the gcc command line) -//#define CONFIGFILE cfg-emisar-d4s.h - -#define USE_LVP // FIXME: won't build when this option is turned off - -// parameters for this defined below or per-driver -#define USE_THERMAL_REGULATION -#define DEFAULT_THERM_CEIL 45 // try not to get hotter than this -#define USE_TENCLICK_THERMAL_CONFIG // ten clicks from off -> thermal config mode - -// short blip when crossing from "click" to "hold" from off -// (helps the user hit moon mode exactly, instead of holding too long -// or too short) -#define MOON_TIMING_HINT -// short blips while ramping -#define BLINK_AT_RAMP_MIDDLE -//#define BLINK_AT_RAMP_FLOOR -#define BLINK_AT_RAMP_CEILING -//#define BLINK_AT_STEPS // whenever a discrete ramp mode is passed in smooth mode - -// ramp down via regular button hold if a ramp-up ended <1s ago -// ("hold, release, hold" ramps down instead of up) -#define USE_REVERSING - -// battery readout style (pick one) -#define BATTCHECK_VpT -//#define BATTCHECK_8bars // FIXME: breaks build -//#define BATTCHECK_4bars // FIXME: breaks build - -// enable beacon mode -#define USE_BEACON_MODE - -// make the ramps configurable by the user -#define USE_RAMP_CONFIG - -/***** specific settings for known driver types *****/ -#include "tk.h" -#include incfile(CONFIGFILE) - - -// thermal properties, if not defined per-driver -#ifndef MIN_THERM_STEPDOWN -#define MIN_THERM_STEPDOWN MAX_1x7135 // lowest value it'll step down to -#endif -#ifndef THERM_FASTER_LEVEL - #ifdef MAX_Nx7135 - #define THERM_FASTER_LEVEL MAX_Nx7135 // throttle back faster when high - #else - #define THERM_FASTER_LEVEL (RAMP_SIZE*4/5) // throttle back faster when high - #endif -#endif -#ifdef USE_THERMAL_REGULATION -#define USE_SET_LEVEL_GRADUALLY // isn't used except for thermal adjustments -#endif - - -/********* Configure SpaghettiMonster *********/ -#define USE_DELAY_ZERO -#define USE_RAMPING -#ifndef RAMP_LENGTH -#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file -#endif -#define USE_BATTCHECK -#define USE_IDLE_MODE // reduce power use while awake and no tasks are pending -#define USE_DYNAMIC_UNDERCLOCKING // cut clock speed at very low modes for better efficiency - -// try to auto-detect how many eeprom bytes -#define USE_EEPROM -#define EEPROM_BYTES_BASE 7 - -#ifdef USE_INDICATOR_LED -#define EEPROM_INDICATOR_BYTES 1 -#else -#define EEPROM_INDICATOR_BYTES 0 -#endif - -#ifdef USE_THERMAL_REGULATION -#define EEPROM_THERMAL_BYTES 2 -#else -#define EEPROM_THERMAL_BYTES 0 -#endif - -#define EEPROM_BYTES (EEPROM_BYTES_BASE+EEPROM_INDICATOR_BYTES+EEPROM_THERMAL_BYTES) - - -#include "spaghetti-monster.h" - - -// FSM states -uint8_t off_state(Event event, uint16_t arg); -// simple numeric entry config menu -uint8_t config_state_base(Event event, uint16_t arg, - uint8_t num_config_steps, - void (*savefunc)()); -#define MAX_CONFIG_VALUES 3 -uint8_t config_state_values[MAX_CONFIG_VALUES]; -// ramping mode and its related config mode -uint8_t steady_state(Event event, uint16_t arg); -#ifdef USE_RAMP_CONFIG -uint8_t ramp_config_state(Event event, uint16_t arg); -#endif -#ifdef USE_BATTCHECK -uint8_t battcheck_state(Event event, uint16_t arg); -#endif -#ifdef USE_THERMAL_REGULATION -#define USE_BLINK_NUM -uint8_t tempcheck_state(Event event, uint16_t arg); -uint8_t thermal_config_state(Event event, uint16_t arg); -#endif -#ifdef USE_BEACON_MODE -// beacon mode and its related config mode -uint8_t beacon_state(Event event, uint16_t arg); -uint8_t beacon_config_state(Event event, uint16_t arg); -#endif -// soft lockout -#define MOON_DURING_LOCKOUT_MODE -// if enabled, 2nd lockout click goes to the other ramp's floor level -//#define LOCKOUT_MOON_FANCY -uint8_t lockout_state(Event event, uint16_t arg); -// momentary / signalling mode -uint8_t momentary_state(Event event, uint16_t arg); - -// general helper function for config modes -uint8_t number_entry_state(Event event, uint16_t arg); -// return value from number_entry_state() -volatile uint8_t number_entry_value; - -void blink_confirm(uint8_t num); -#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY) -void indicator_blink(uint8_t arg); -#endif - -// remember stuff even after battery was changed -void load_config(); -void save_config(); - -// default ramp options if not overridden earlier per-driver -#ifndef RAMP_SMOOTH_FLOOR - #define RAMP_SMOOTH_FLOOR 1 -#endif -#ifndef RAMP_SMOOTH_CEIL - #if PWM_CHANNELS == 3 - #define RAMP_SMOOTH_CEIL MAX_Nx7135 - #else - #define RAMP_SMOOTH_CEIL MAX_LEVEL - 30 - #endif -#endif -#ifndef RAMP_DISCRETE_FLOOR - #define RAMP_DISCRETE_FLOOR 20 -#endif -#ifndef RAMP_DISCRETE_CEIL - #define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL -#endif -#ifndef RAMP_DISCRETE_STEPS - #define RAMP_DISCRETE_STEPS 7 -#endif - -// mile marker(s) partway up the ramp -// default: blink only at border between regulated and FET -#ifdef BLINK_AT_RAMP_MIDDLE - #if PWM_CHANNELS >= 3 - #ifndef BLINK_AT_RAMP_MIDDLE_1 - #define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135 - #ifndef BLINK_AT_RAMP_MIDDLE_2 - #define BLINK_AT_RAMP_MIDDLE_2 MAX_1x7135 - #endif - #endif - #else - #ifndef BLINK_AT_RAMP_MIDDLE_1 - #define BLINK_AT_RAMP_MIDDLE_1 MAX_1x7135 - #endif - #endif -#endif - -// brightness control -#ifndef DEFAULT_LEVEL -#define DEFAULT_LEVEL MAX_1x7135 -#endif -uint8_t memorized_level = DEFAULT_LEVEL; -// smooth vs discrete ramping -volatile uint8_t ramp_style = 0; // 0 = smooth, 1 = discrete -volatile uint8_t ramp_smooth_floor = RAMP_SMOOTH_FLOOR; -volatile uint8_t ramp_smooth_ceil = RAMP_SMOOTH_CEIL; -volatile uint8_t ramp_discrete_floor = RAMP_DISCRETE_FLOOR; -volatile uint8_t ramp_discrete_ceil = RAMP_DISCRETE_CEIL; -volatile uint8_t ramp_discrete_steps = RAMP_DISCRETE_STEPS; -uint8_t ramp_discrete_step_size; // don't set this - -#ifdef USE_INDICATOR_LED - // bits 2-3 control lockout mode - // bits 0-1 control "off" mode - // modes are: 0=off, 1=low, 2=high, 3=blinking (if TICK_DURING_STANDBY enabled) - #ifdef INDICATOR_LED_DEFAULT_MODE - uint8_t indicator_led_mode = INDICATOR_LED_DEFAULT_MODE; - #else - #ifdef USE_INDICATOR_LED_WHILE_RAMPING - //uint8_t indicator_led_mode = (1<<2) + 2; - uint8_t indicator_led_mode = (2<<2) + 1; - #else - uint8_t indicator_led_mode = (3<<2) + 1; - #endif - #endif -#endif - -// calculate the nearest ramp level which would be valid at the moment -// (is a no-op for smooth ramp, but limits discrete ramp to only the -// correct levels for the user's config) -uint8_t nearest_level(int16_t target); - -#ifdef USE_THERMAL_REGULATION -// brightness before thermal step-down -uint8_t target_level = 0; -#endif - -#ifdef USE_BEACON_MODE -// beacon timing -volatile uint8_t beacon_seconds = 2; -#endif - - -uint8_t off_state(Event event, uint16_t arg) { - // turn emitter off when entering state - if (event == EV_enter_state) { - set_level(0); - #ifdef USE_INDICATOR_LED - indicator_led(indicator_led_mode & 0x03); - #endif - // sleep while off (lower power use) - go_to_standby = 1; - return EVENT_HANDLED; - } - // go back to sleep eventually if we got bumped but didn't leave "off" state - else if (event == EV_tick) { - if (arg > TICKS_PER_SECOND*2) { - go_to_standby = 1; - #ifdef USE_INDICATOR_LED - indicator_led(indicator_led_mode & 0x03); - #endif - } - return EVENT_HANDLED; - } - #if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED) - // blink the indicator LED, maybe - else if (event == EV_sleep_tick) { - if ((indicator_led_mode & 0b00000011) == 0b00000011) { - indicator_blink(arg); - } - return EVENT_HANDLED; - } - #endif - // hold (initially): go to lowest level (floor), but allow abort for regular click - else if (event == EV_click1_press) { - set_level(nearest_level(1)); - return EVENT_HANDLED; - } - // hold: go to lowest level - else if (event == EV_click1_hold) { - #ifdef MOON_TIMING_HINT - if (arg == 0) { - // let the user know they can let go now to stay at moon - uint8_t temp = actual_level; - set_level(0); - delay_4ms(3); - set_level(temp); - } else - #endif - // don't start ramping immediately; - // give the user time to release at moon level - //if (arg >= HOLD_TIMEOUT) { // smaller - if (arg >= (!ramp_style) * HOLD_TIMEOUT) { // more consistent - set_state(steady_state, 1); - } - return EVENT_HANDLED; - } - // hold, release quickly: go to lowest level (floor) - else if (event == EV_click1_hold_release) { - set_state(steady_state, 1); - return EVENT_HANDLED; - } - // 1 click (before timeout): go to memorized level, but allow abort for double click - else if (event == EV_click1_release) { - set_level(nearest_level(memorized_level)); - return EVENT_HANDLED; - } - // 1 click: regular mode - else if (event == EV_1click) { - set_state(steady_state, memorized_level); - return EVENT_HANDLED; - } - // click, hold: go to highest level (ceiling) (for ramping down) - else if (event == EV_click2_hold) { - set_state(steady_state, MAX_LEVEL); - return EVENT_HANDLED; - } - // 2 clicks: highest mode (ceiling) - else if (event == EV_2clicks) { - set_state(steady_state, MAX_LEVEL); - return EVENT_HANDLED; - } - // 3 clicks (initial press): off, to prep for later events - else if (event == EV_click3_press) { - set_level(0); - return EVENT_HANDLED; - } - #ifdef USE_BATTCHECK - // 3 clicks: battcheck mode / blinky mode group 1 - else if (event == EV_3clicks) { - set_state(battcheck_state, 0); - return EVENT_HANDLED; - } - #endif - // 4 clicks: momentary - else if (event == EV_4clicks) { - blink_confirm(1); - set_state(momentary_state, 0); - return EVENT_HANDLED; - } - // 6 clicks: lockout mode - else if (event == EV_6clicks) { - blink_confirm(2); - set_state(lockout_state, 0); - return EVENT_HANDLED; - } - #ifdef USE_INDICATOR_LED - // 7 clicks: next aux LED mode - else if (event == EV_7clicks) { - blink_confirm(1); - uint8_t mode = (indicator_led_mode & 3) + 1; - #ifdef TICK_DURING_STANDBY - mode = mode & 3; - #else - mode = mode % 3; - #endif - #ifdef INDICATOR_LED_SKIP_LOW - if (mode == 1) { mode ++; } - #endif - indicator_led_mode = (indicator_led_mode & 0b11111100) | mode; - indicator_led(mode); - save_config(); - return EVENT_HANDLED; - } - #endif - // 8 clicks: beacon mode - else if (event == EV_8clicks) { - set_state(beacon_state, 0); - return EVENT_HANDLED; - } - #ifdef USE_TENCLICK_THERMAL_CONFIG - // 10 clicks: thermal config mode - else if (event == EV_10clicks) { - push_state(thermal_config_state, 0); - return EVENT_HANDLED; - } - #endif - return EVENT_NOT_HANDLED; -} - - -uint8_t steady_state(Event event, uint16_t arg) { - uint8_t mode_min = ramp_smooth_floor; - uint8_t mode_max = ramp_smooth_ceil; - uint8_t ramp_step_size = 1; - #ifdef USE_REVERSING - static int8_t ramp_direction = 1; - #endif - if (ramp_style) { - mode_min = ramp_discrete_floor; - mode_max = ramp_discrete_ceil; - ramp_step_size = ramp_discrete_step_size; - } - - // turn LED on when we first enter the mode - if ((event == EV_enter_state) || (event == EV_reenter_state)) { - // if we just got back from config mode, go back to memorized level - if (event == EV_reenter_state) { - arg = memorized_level; - } - // remember this level, unless it's moon or turbo - if ((arg > mode_min) && (arg < mode_max)) - memorized_level = arg; - // use the requested level even if not memorized - arg = nearest_level(arg); - #ifdef USE_THERMAL_REGULATION - target_level = arg; - #endif - set_level(arg); - #ifdef USE_REVERSING - ramp_direction = 1; - #endif - return EVENT_HANDLED; - } - // 1 click: off - else if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: go to/from highest level - else if (event == EV_2clicks) { - if (actual_level < MAX_LEVEL) { - #ifdef USE_THERMAL_REGULATION - target_level = MAX_LEVEL; - #endif - // true turbo, not the mode-specific ceiling - set_level(MAX_LEVEL); - } - else { - #ifdef USE_THERMAL_REGULATION - target_level = memorized_level; - #endif - set_level(memorized_level); - } - return EVENT_HANDLED; - } - // 3 clicks: toggle smooth vs discrete ramping - else if (event == EV_3clicks) { - ramp_style = !ramp_style; - memorized_level = nearest_level(actual_level); - #ifdef USE_THERMAL_REGULATION - target_level = memorized_level; - #ifdef USE_SET_LEVEL_GRADUALLY - //set_level_gradually(lvl); - #endif - #endif - save_config(); - set_level(0); - delay_4ms(20/4); - set_level(memorized_level); - return EVENT_HANDLED; - } - #ifdef USE_RAMP_CONFIG - // 4 clicks: configure this ramp mode - else if (event == EV_4clicks) { - push_state(ramp_config_state, 0); - return EVENT_HANDLED; - } - #endif - // hold: change brightness (brighter) - else if (event == EV_click1_hold) { - // ramp slower in discrete mode - if (ramp_style && (arg % HOLD_TIMEOUT != 0)) { - return EVENT_HANDLED; - } - #ifdef USE_REVERSING - // make it ramp down instead, if already at max - if ((arg <= 1) && (actual_level >= mode_max)) { - ramp_direction = -1; - } - memorized_level = nearest_level((int16_t)actual_level \ - + (ramp_step_size * ramp_direction)); - #else - memorized_level = nearest_level((int16_t)actual_level + ramp_step_size); - #endif - #ifdef USE_THERMAL_REGULATION - target_level = memorized_level; - #endif - #if defined(BLINK_AT_RAMP_CEILING) || defined(BLINK_AT_RAMP_MIDDLE) - // only blink once for each threshold - if ((memorized_level != actual_level) && ( - 0 // for easier syntax below - #ifdef BLINK_AT_RAMP_MIDDLE_1 - || (memorized_level == BLINK_AT_RAMP_MIDDLE_1) - #endif - #ifdef BLINK_AT_RAMP_MIDDLE_2 - || (memorized_level == BLINK_AT_RAMP_MIDDLE_2) - #endif - #ifdef BLINK_AT_RAMP_CEILING - || (memorized_level == mode_max) - #endif - #if defined(USE_REVERSING) && defined(BLINK_AT_RAMP_FLOOR) - || (memorized_level == mode_min) - #endif - )) { - set_level(0); - delay_4ms(8/4); - } - #endif - #if defined(BLINK_AT_STEPS) - uint8_t foo = ramp_style; - ramp_style = 1; - uint8_t nearest = nearest_level((int16_t)actual_level); - ramp_style = foo; - // only blink once for each threshold - if ((memorized_level != actual_level) && - (ramp_style == 0) && - (memorized_level == nearest) - ) - { - set_level(0); - delay_4ms(8/4); - } - #endif - set_level(memorized_level); - return EVENT_HANDLED; - } - #if defined(USE_REVERSING) - // reverse ramp direction on hold release - else if (event == EV_click1_hold_release) { - #ifdef USE_REVERSING - ramp_direction = -ramp_direction; - #endif - return EVENT_HANDLED; - } - #endif - // click, hold: change brightness (dimmer) - else if (event == EV_click2_hold) { - #ifdef USE_REVERSING - ramp_direction = 1; - #endif - // ramp slower in discrete mode - if (ramp_style && (arg % HOLD_TIMEOUT != 0)) { - return EVENT_HANDLED; - } - // TODO? make it ramp up instead, if already at min? - memorized_level = nearest_level((int16_t)actual_level - ramp_step_size); - #ifdef USE_THERMAL_REGULATION - target_level = memorized_level; - #endif - #if defined(BLINK_AT_RAMP_FLOOR) || defined(BLINK_AT_RAMP_MIDDLE) - // only blink once for each threshold - if ((memorized_level != actual_level) && ( - 0 // for easier syntax below - #ifdef BLINK_AT_RAMP_MIDDLE_1 - || (memorized_level == BLINK_AT_RAMP_MIDDLE_1) - #endif - #ifdef BLINK_AT_RAMP_MIDDLE_2 - || (memorized_level == BLINK_AT_RAMP_MIDDLE_2) - #endif - #ifdef BLINK_AT_RAMP_FLOOR - || (memorized_level == mode_min) - #endif - )) { - set_level(0); - delay_4ms(8/4); - } - #endif - #if defined(BLINK_AT_STEPS) - uint8_t foo = ramp_style; - ramp_style = 1; - uint8_t nearest = nearest_level((int16_t)actual_level); - ramp_style = foo; - // only blink once for each threshold - if ((memorized_level != actual_level) && - (ramp_style == 0) && - (memorized_level == nearest) - ) - { - set_level(0); - delay_4ms(8/4); - } - #endif - set_level(memorized_level); - return EVENT_HANDLED; - } - #if defined(USE_SET_LEVEL_GRADUALLY) || defined(USE_REVERSING) - else if (event == EV_tick) { - #ifdef USE_REVERSING - // un-reverse after 1 second - if (arg == TICKS_PER_SECOND) ramp_direction = 1; - #endif - #ifdef USE_SET_LEVEL_GRADUALLY - // make thermal adjustment speed scale with magnitude - if ((arg & 1) && (actual_level < THERM_FASTER_LEVEL)) { - return EVENT_HANDLED; // adjust slower when not a high mode - } - #ifdef THERM_HARD_TURBO_DROP - else if ((! (actual_level < THERM_FASTER_LEVEL)) - && (actual_level > gradual_target)) { - gradual_tick(); - } - else { - #endif - // [int(62*4 / (x**0.8)) for x in (1,2,4,8,16,32,64,128)] - //uint8_t intervals[] = {248, 142, 81, 46, 26, 15, 8, 5}; - // [int(62*4 / (x**0.9)) for x in (1,2,4,8,16,32,64,128)] - //uint8_t intervals[] = {248, 132, 71, 38, 20, 10, 5, 3}; - // [int(62*4 / (x**0.95)) for x in (1,2,4,8,16,32,64,128)] - uint8_t intervals[] = {248, 128, 66, 34, 17, 9, 4, 2}; - uint8_t diff; - static uint8_t ticks_since_adjust = 0; - ticks_since_adjust ++; - if (gradual_target > actual_level) diff = gradual_target - actual_level; - else { - diff = actual_level - gradual_target; - } - uint8_t magnitude = 0; - #ifndef THERM_HARD_TURBO_DROP - // if we're on a really high mode, drop faster - if (actual_level >= THERM_FASTER_LEVEL) { magnitude ++; } - #endif - while (diff) { - magnitude ++; - diff >>= 1; - } - uint8_t ticks_per_adjust = intervals[magnitude]; - if (ticks_since_adjust > ticks_per_adjust) - { - gradual_tick(); - ticks_since_adjust = 0; - } - //if (!(arg % ticks_per_adjust)) gradual_tick(); - #ifdef THERM_HARD_TURBO_DROP - } - #endif - #endif - return EVENT_HANDLED; - } - #endif - #ifdef USE_THERMAL_REGULATION - // overheating: drop by an amount proportional to how far we are above the ceiling - else if (event == EV_temperature_high) { - #if 0 - uint8_t foo = actual_level; - set_level(0); - delay_4ms(2); - set_level(foo); - #endif - #ifdef THERM_HARD_TURBO_DROP - if (actual_level > THERM_FASTER_LEVEL) { - #ifdef USE_SET_LEVEL_GRADUALLY - set_level_gradually(THERM_FASTER_LEVEL); - #else - set_level(THERM_FASTER_LEVEL); - #endif - target_level = THERM_FASTER_LEVEL; - } else - #endif - if (actual_level > MIN_THERM_STEPDOWN) { - int16_t stepdown = actual_level - arg; - if (stepdown < MIN_THERM_STEPDOWN) stepdown = MIN_THERM_STEPDOWN; - else if (stepdown > MAX_LEVEL) stepdown = MAX_LEVEL; - #ifdef USE_SET_LEVEL_GRADUALLY - set_level_gradually(stepdown); - #else - set_level(stepdown); - #endif - } - return EVENT_HANDLED; - } - // underheating: increase slowly if we're lower than the target - // (proportional to how low we are) - else if (event == EV_temperature_low) { - #if 0 - uint8_t foo = actual_level; - set_level(0); - delay_4ms(2); - set_level(foo); - #endif - if (actual_level < target_level) { - //int16_t stepup = actual_level + (arg>>1); - int16_t stepup = actual_level + arg; - if (stepup > target_level) stepup = target_level; - else if (stepup < MIN_THERM_STEPDOWN) stepup = MIN_THERM_STEPDOWN; - #ifdef USE_SET_LEVEL_GRADUALLY - set_level_gradually(stepup); - #else - set_level(stepup); - #endif - } - return EVENT_HANDLED; - } - #endif - return EVENT_NOT_HANDLED; -} - - -#ifdef USE_BATTCHECK -uint8_t battcheck_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: tempcheck mode - else if (event == EV_2clicks) { - set_state(tempcheck_state, 0); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} -#endif - - -#ifdef USE_THERMAL_REGULATION -uint8_t tempcheck_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 4 clicks: thermal config mode - else if (event == EV_4clicks) { - push_state(thermal_config_state, 0); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} -#endif - - -#ifdef USE_BEACON_MODE -uint8_t beacon_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // TODO: use sleep ticks to measure time between pulses, - // to save power - // 4 clicks: beacon config mode - else if (event == EV_4clicks) { - push_state(beacon_config_state, 0); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} -#endif // #ifdef USE_BEACON_MODE - - -uint8_t lockout_state(Event event, uint16_t arg) { - #ifdef MOON_DURING_LOCKOUT_MODE - // momentary(ish) moon mode during lockout - // button is being held - if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { - #ifdef LOCKOUT_MOON_LOWEST - // Use lowest moon configured - uint8_t lvl = ramp_smooth_floor; - if (ramp_discrete_floor < lvl) lvl = ramp_discrete_floor; - set_level(lvl); - #elif defined(LOCKOUT_MOON_FANCY) - uint8_t levels[] = { ramp_smooth_floor, ramp_discrete_floor }; - if ((event & 0x0f) == 2) { - set_level(levels[ramp_style^1]); - } else { - set_level(levels[ramp_style]); - } - #else - // Use moon from current ramp - set_level(nearest_level(1)); - #endif - } - // button was released - else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { - set_level(0); - } - #endif - - // regular event handling - // conserve power while locked out - // (allow staying awake long enough to exit, but otherwise - // be persistent about going back to sleep every few seconds - // even if the user keeps pressing the button) - #ifdef USE_INDICATOR_LED - if (event == EV_enter_state) { - indicator_led(indicator_led_mode >> 2); - } else - #endif - if (event == EV_tick) { - if (arg > TICKS_PER_SECOND*2) { - go_to_standby = 1; - #ifdef USE_INDICATOR_LED - indicator_led(indicator_led_mode >> 2); - #endif - } - return EVENT_HANDLED; - } - #if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED) - else if (event == EV_sleep_tick) { - if ((indicator_led_mode & 0b00001100) == 0b00001100) { - indicator_blink(arg); - } - return EVENT_HANDLED; - } - #endif - #ifdef USE_INDICATOR_LED - // 3 clicks: rotate through indicator LED modes (lockout mode) - else if (event == EV_3clicks) { - uint8_t mode = indicator_led_mode >> 2; - #ifdef TICK_DURING_STANDBY - mode = (mode + 1) & 3; - #else - mode = (mode + 1) % 3; - #endif - #ifdef INDICATOR_LED_SKIP_LOW - if (mode == 1) { mode ++; } - #endif - indicator_led_mode = (mode << 2) + (indicator_led_mode & 0x03); - indicator_led(mode); - save_config(); - return EVENT_HANDLED; - } - #endif - // 6 clicks: exit - else if (event == EV_6clicks) { - blink_confirm(1); - set_state(off_state, 0); - return EVENT_HANDLED; - } - - return EVENT_NOT_HANDLED; -} - - -uint8_t momentary_state(Event event, uint16_t arg) { - // TODO: momentary strobe here? (for light painting) - - // light up when the button is pressed; go dark otherwise - // button is being held - if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { - set_level(memorized_level); - return EVENT_HANDLED; - } - // button was released - else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { - set_level(0); - //go_to_standby = 1; // sleep while light is off - return EVENT_HANDLED; - } - - // Sleep, dammit! (but wait a few seconds first) - // (because standby mode uses such little power that it can interfere - // with exiting via tailcap loosen+tighten unless you leave power - // disconnected for several seconds, so we want to be awake when that - // happens to speed up the process) - else if ((event == EV_tick) && (actual_level == 0)) { - if (arg > TICKS_PER_SECOND*15) { // sleep after 15 seconds - go_to_standby = 1; // sleep while light is off - // TODO: lighted button should use lockout config? - } - return EVENT_HANDLED; - } - - return EVENT_NOT_HANDLED; -} - - -// ask the user for a sequence of numbers, then save them and return to caller -uint8_t config_state_base(Event event, uint16_t arg, - uint8_t num_config_steps, - void (*savefunc)()) { - static uint8_t config_step; - if (event == EV_enter_state) { - config_step = 0; - set_level(0); - return EVENT_HANDLED; - } - // advance forward through config steps - else if (event == EV_tick) { - if (config_step < num_config_steps) { - push_state(number_entry_state, config_step + 1); - } - else { - // TODO: blink out some sort of success pattern - savefunc(); - save_config(); - //set_state(retstate, retval); - pop_state(); - } - return EVENT_HANDLED; - } - // an option was set (return from number_entry_state) - else if (event == EV_reenter_state) { - config_state_values[config_step] = number_entry_value; - config_step ++; - return EVENT_HANDLED; - } - //return EVENT_NOT_HANDLED; - // eat all other events; don't pass any through to parent - return EVENT_HANDLED; -} - -#ifdef USE_RAMP_CONFIG -void ramp_config_save() { - // parse values - uint8_t val; - if (ramp_style) { // discrete / stepped ramp - - val = config_state_values[0]; - if (val) { ramp_discrete_floor = val; } - - val = config_state_values[1]; - if (val) { ramp_discrete_ceil = MAX_LEVEL + 1 - val; } - - val = config_state_values[2]; - if (val) ramp_discrete_steps = val; - - } else { // smooth ramp - - val = config_state_values[0]; - if (val) { ramp_smooth_floor = val; } - - val = config_state_values[1]; - if (val) { ramp_smooth_ceil = MAX_LEVEL + 1 - val; } - - } -} - -uint8_t ramp_config_state(Event event, uint16_t arg) { - uint8_t num_config_steps; - num_config_steps = 2 + ramp_style; - return config_state_base(event, arg, - num_config_steps, ramp_config_save); -} -#endif // #ifdef USE_RAMP_CONFIG - - -#ifdef USE_THERMAL_REGULATION -void thermal_config_save() { - // parse values - uint8_t val; - - // calibrate room temperature - val = config_state_values[0]; - if (val) { - int8_t rawtemp = temperature - therm_cal_offset; - therm_cal_offset = val - rawtemp; - reset_thermal_history = 1; // invalidate all recent temperature data - } - - val = config_state_values[1]; - if (val) { - // set maximum heat limit - therm_ceil = 30 + val - 1; - } - if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL; -} - -uint8_t thermal_config_state(Event event, uint16_t arg) { - return config_state_base(event, arg, - 2, thermal_config_save); -} -#endif // #ifdef USE_THERMAL_REGULATION - - -#ifdef USE_BEACON_MODE -void beacon_config_save() { - // parse values - uint8_t val = config_state_values[0]; - if (val) { - beacon_seconds = val; - } -} - -uint8_t beacon_config_state(Event event, uint16_t arg) { - return config_state_base(event, arg, - 1, beacon_config_save); -} - -inline void beacon_mode_iter() { - // one iteration of main loop() - set_level(memorized_level); - nice_delay_ms(100); - set_level(0); - nice_delay_ms(((beacon_seconds) * 1000) - 100); -} -#endif // #ifdef USE_BEACON_MODE - - -uint8_t number_entry_state(Event event, uint16_t arg) { - static uint8_t value; - static uint8_t blinks_left; - static uint8_t entry_step; - static uint16_t wait_ticks; - if (event == EV_enter_state) { - value = 0; - blinks_left = arg; - entry_step = 0; - wait_ticks = 0; - return EVENT_HANDLED; - } - // advance through the process: - // 0: wait a moment - // 1: blink out the 'arg' value - // 2: wait a moment - // 3: "buzz" while counting clicks - // 4: save and exit - else if (event == EV_tick) { - // wait a moment - if ((entry_step == 0) || (entry_step == 2)) { - if (wait_ticks < TICKS_PER_SECOND/2) - wait_ticks ++; - else { - entry_step ++; - wait_ticks = 0; - } - } - // blink out the option number - else if (entry_step == 1) { - if (blinks_left) { - if ((wait_ticks & 31) == 10) { - set_level(RAMP_SIZE/4); - } - else if ((wait_ticks & 31) == 20) { - set_level(0); - } - else if ((wait_ticks & 31) == 31) { - blinks_left --; - } - wait_ticks ++; - } - else { - entry_step ++; - wait_ticks = 0; - } - } - else if (entry_step == 3) { // buzz while waiting for a number to be entered - wait_ticks ++; - // buzz for N seconds after last event - if ((wait_ticks & 3) == 0) { - set_level(RAMP_SIZE/6); - } - else if ((wait_ticks & 3) == 2) { - set_level(RAMP_SIZE/8); - } - // time out after 3 seconds - if (wait_ticks > TICKS_PER_SECOND*3) { - //number_entry_value = value; - set_level(0); - entry_step ++; - } - } - else if (entry_step == 4) { - number_entry_value = value; - pop_state(); - } - return EVENT_HANDLED; - } - // count clicks - else if (event == EV_click1_release) { - empty_event_sequence(); - if (entry_step == 3) { // only count during the "buzz" - value ++; - wait_ticks = 0; - // flash briefly - set_level(RAMP_SIZE/2); - delay_4ms(8/2); - set_level(0); - } - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - - -// find the ramp level closest to the target, -// using only the levels which are allowed in the current state -uint8_t nearest_level(int16_t target) { - // bounds check - // using int16_t here saves us a bunch of logic elsewhere, - // by allowing us to correct for numbers < 0 or > 255 in one central place - uint8_t mode_min = ramp_smooth_floor; - uint8_t mode_max = ramp_smooth_ceil; - if (ramp_style) { - mode_min = ramp_discrete_floor; - mode_max = ramp_discrete_ceil; - } - if (target < mode_min) return mode_min; - if (target > mode_max) return mode_max; - // the rest isn't relevant for smooth ramping - if (! ramp_style) return target; - - uint8_t ramp_range = ramp_discrete_ceil - ramp_discrete_floor; - ramp_discrete_step_size = ramp_range / (ramp_discrete_steps-1); - uint8_t this_level = ramp_discrete_floor; - - for(uint8_t i=0; i>1)) - return this_level; - } - return this_level; -} - - -void blink_confirm(uint8_t num) { - for (; num>0; num--) { - set_level(MAX_LEVEL/4); - delay_4ms(10/4); - set_level(0); - delay_4ms(100/4); - } -} - - -#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY) -// beacon-like mode for the indicator LED -void indicator_blink(uint8_t arg) { - #define USE_FANCIER_BLINKING_INDICATOR - #ifdef USE_FANCIER_BLINKING_INDICATOR - - // fancy blink, set off/low/high levels here: - uint8_t seq[] = {0, 1, 2, 1, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0}; - indicator_led(seq[arg & 15]); - - #else // basic blink, 1/8th duty cycle - - if (! (arg & 7)) { - indicator_led(2); - } - else { - indicator_led(0); - } - - #endif -} -#endif - - -void load_config() { - if (load_eeprom()) { - ramp_style = eeprom[0]; - #ifdef USE_RAMP_CONFIG - ramp_smooth_floor = eeprom[1]; - ramp_smooth_ceil = eeprom[2]; - ramp_discrete_floor = eeprom[3]; - ramp_discrete_ceil = eeprom[4]; - ramp_discrete_steps = eeprom[5]; - #endif - #ifdef USE_BEACON_MODE - beacon_seconds = eeprom[6]; - #endif - #ifdef USE_THERMAL_REGULATION - therm_ceil = eeprom[EEPROM_BYTES_BASE]; - therm_cal_offset = eeprom[EEPROM_BYTES_BASE+1]; - #endif - #ifdef USE_INDICATOR_LED - indicator_led_mode = eeprom[EEPROM_BYTES_BASE+EEPROM_THERMAL_BYTES]; - #endif - } -} - -void save_config() { - eeprom[0] = ramp_style; - #ifdef USE_RAMP_CONFIG - eeprom[1] = ramp_smooth_floor; - eeprom[2] = ramp_smooth_ceil; - eeprom[3] = ramp_discrete_floor; - eeprom[4] = ramp_discrete_ceil; - eeprom[5] = ramp_discrete_steps; - #endif - #ifdef USE_BEACON_MODE - eeprom[6] = beacon_seconds; - #endif - #ifdef USE_THERMAL_REGULATION - eeprom[EEPROM_BYTES_BASE ] = therm_ceil; - eeprom[EEPROM_BYTES_BASE+1] = therm_cal_offset; - #endif - #ifdef USE_INDICATOR_LED - eeprom[EEPROM_BYTES_BASE+EEPROM_THERMAL_BYTES] = indicator_led_mode; - #endif - - save_eeprom(); -} - -void low_voltage() { - StatePtr state = current_state; - - // TODO: turn off aux LED(s) when power is really low - - if (0) {} // placeholder - - // in normal mode, step down or turn off - else if (state == steady_state) { - if (actual_level > 1) { - uint8_t lvl = (actual_level >> 1) + (actual_level >> 2); - set_level(lvl); - #ifdef USE_THERMAL_REGULATION - target_level = lvl; - #ifdef USE_SET_LEVEL_GRADUALLY - // not needed? - //set_level_gradually(lvl); - #endif - #endif - } - else { - set_state(off_state, 0); - } - } - // all other modes, just turn off when voltage is low - else { - set_state(off_state, 0); - } -} - - -void setup() { - // blink at power-on to let user know power is connected - set_level(RAMP_SIZE/8); - delay_4ms(3); - set_level(0); - - load_config(); - - push_state(off_state, 0); -} - - -void loop() { - - StatePtr state = current_state; - - if (0) {} - - #ifdef USE_BATTCHECK - else if (state == battcheck_state) { - battcheck(); - } - #endif - - #ifdef USE_BEACON_MODE - else if (state == beacon_state) { - beacon_mode_iter(); - } - #endif - - #ifdef USE_THERMAL_REGULATION - // TODO: blink out therm_ceil during thermal_config_state? - else if (state == tempcheck_state) { - blink_num(temperature); - nice_delay_ms(1000); - } - #endif - - #ifdef USE_IDLE_MODE - else { - // doze until next clock tick - idle_mode(); - } - #endif - -} diff --git a/spaghetti-monster/rampingios/rampingiosv3.svg b/spaghetti-monster/rampingios/rampingiosv3.svg deleted file mode 100644 index bc9e6b3..0000000 --- a/spaghetti-monster/rampingios/rampingiosv3.svg +++ /dev/null @@ -1,4113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - Ramps: - - - ThermalCfg - - - - - BeaconCfg - - - - - - Ramp - Ceil - Floor - - - Turbo - - - - - Mem - Regulated Hybrid -------------- Direct Drive - - - - - - - - - - - - Ramp - - Cfg - - - - - - Actions - 1 Fast Click - Hold - 3 Fast Clicks - Other Action - - - - 2 Fast Clicks - Click, Hold - RampingIOS V3 - - - - 7 Clicks - - - - OFF - - - - OFF - - - - - - - 4 Clicks - 4 Clicks - Click, Click, Hold - - 6 Clicks - - - - Smooth - - - - Ramp Cfg - - 4 Clicks - - - - 4 Clicks - - - - 1. Floor (click N times for level N)2. Ceiling (click N times for 1 + Turbo - N)3. Number of steps (stepped ramp only) - 1. Current temperature (click N times for N deg C)2. Temperature limit (click N times for 30 C + N) - 1. Beacon speed (click N times for N seconds per flash) - Thermal Cfg - Beacon Cfg - - 4 Clicks - - - - - - Stepped - - - - Tactical - - - BattCheck - - Lockout - - TempCheck - Beacon - - ThermalCfg - - - OFF - - - - - - - - - - - (momentary) - 3 Clicks - 4 Clicks - 6 Clicks - 8 Clicks - 10 Clicks - - - Aux LED - mode - next - - - - - - - - lockout LED - mode - next - 4 Clicks - 4 Clicks - 4 Clicks - - - - - diff --git a/spaghetti-monster/spaghetti-monster.h b/spaghetti-monster/spaghetti-monster.h deleted file mode 100644 index 77431f8..0000000 --- a/spaghetti-monster/spaghetti-monster.h +++ /dev/null @@ -1,75 +0,0 @@ -// spaghetti-monster.h: UI toolkit / microkernel for e-switch flashlights. -// Copyright (C) 2017-2023 Selene ToyKeeper -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -/* - * SpaghettiMonster: Generic foundation code for e-switch flashlights. - * Other possible names: - * - FSM - * - RoundTable - * - Mostly Harmless - * - ... - */ - -#include "tk-attiny.h" - -#include -#include - -// include project definitions to help with recognizing symbols -#include "fsm-events.h" -#include "fsm-states.h" -#include "fsm-adc.h" -#include "fsm-wdt.h" -#include "fsm-pcint.h" -#include "fsm-standby.h" -#include "fsm-channels.h" -#include "fsm-ramping.h" -#include "fsm-random.h" -#ifdef USE_EEPROM -#include "fsm-eeprom.h" -#endif -#include "fsm-misc.h" -#include "fsm-main.h" - -#if defined(USE_DELAY_MS) || defined(USE_DELAY_4MS) || defined(USE_DELAY_ZERO) || defined(USE_DEBUG_BLINK) -#define OWN_DELAY -#include "tk-delay.h" -#endif - -#ifdef USE_DEBUG_BLINK -#define DEBUG_FLASH PWM1_LVL = 64; delay_4ms(2); PWM1_LVL = 0; -void debug_blink(uint8_t num) { - for(; num>0; num--) { - PWM1_LVL = 32; - delay_4ms(100/4); - PWM1_LVL = 0; - delay_4ms(100/4); - } -} -#endif - -// Define these in your SpaghettiMonster recipe -// boot-time tasks -void setup(); -// single loop iteration, runs continuously -void loop(); - -// include executable functions too, for easier compiling -#include "fsm-states.c" -#include "fsm-events.c" -#include "fsm-adc.c" -#include "fsm-wdt.c" -#include "fsm-pcint.c" -#include "fsm-standby.c" -#include "fsm-channels.c" -#include "fsm-ramping.c" -#include "fsm-random.c" -#ifdef USE_EEPROM -#include "fsm-eeprom.c" -#endif -#include "fsm-misc.c" -#include "fsm-main.c" - diff --git a/spaghetti-monster/spaghetti-monster.txt b/spaghetti-monster/spaghetti-monster.txt deleted file mode 100644 index 434e1bc..0000000 --- a/spaghetti-monster/spaghetti-monster.txt +++ /dev/null @@ -1,325 +0,0 @@ -Spaghetti Monster: A UI toolkit library for flashlights -------------------------------------------------------- - -This toolkit takes care of most of the obnoxious parts of dealing with -tiny embedded chips and flashlight hardware, leaving you to focus on the -interface and user-visible features. - -For a quick start, look at the example UIs provided to see how things -are done. They are probably the most useful reference. However, other -details can be found here or in the FSM source code. - - -Why is it called Spaghetti Monster? - - This toolkit is a finite state machine, or FSM. Another thing FSM - stands for is Flying Spaghetti Monster. Source code tends to weave - into intricate knots like spaghetti, called spaghetti code, - particularly when the code isn't using appropriate abstractions for - the task it implements. - - Prior e-switch light code had a tendency to get pretty spaghetti-like, - and it made the code difficult to write, understand, and modify. So I - started from scratch and logically separated the hardware details from - the UI. This effectively put the spaghetti monster in a box, put it - on a leash, to make it behave and stay out of the way while we focus - on the user interface. - - Also, it's just kind of a fun name. :) - - -General concept: - - Spaghetti Monster (FSM) implements a stack-based finite state machine - with an event-handling system. - - Each FSM program should have a setup() function, a loop() function, - and at least one State: - - - The setup() function runs once each time power is connected. - - - The loop() function is called repeatedly whenever the system is - otherwise idle. Put your long-running tasks here, preferably with - consideration taken to allow for cooperative multitasking. - - - The States on the stack will be called whenever an event happens. - States are called in top-to-bottom order until a state returns an - "EVENT_HANDLED" signal. Only do quick tasks here. - - -Finite State Machine: - - Each "State" is simply a callback function which handles events. It - should return EVENT_HANDLED for each event type it does something - with, or EVENT_NOT_HANDLED otherwise. - - Transitions between states typically involve mapping an Event to a new - State, such as this: - - // 3 clicks: go to strobe modes - else if (event == EV_3clicks) { - set_state(strobe_state, 0); - return EVENT_HANDLED; - } - - It is strongly recommended that your State functions never do anything - which takes more than a few milliseconds... and certainly not longer - than 16ms. If you do this, the pending events may pile up to the - point where new events get thrown away. So, do only quick tasks in - the event handler, and do your longer-running tasks in the loop() - function instead. Preferably with precautions taken to allow for - cooperative multitasking. - - If your State function takes longer than one WDT tick (16ms) once in a - while, the system won't break. Several events can be queued. But be - sure not to do it very often. - - Several state management functions are provided: - - - set_state(new_state, arg): Replace the current state on the stack. - Send 'arg' to the new state for its init event. - - - push_state(new_state, arg): Add a new state to the stack, leaving - the current state below it. Send 'arg' to the new state for its - init event. - - - pop_state(): Get rid of (and return) the top-most state. Re-enter - the state below. - - -Event types: - - Event types are defined in fsm-events.h. You may want to adjust these - to fit your program, but the defaults are: - - State transitions: - - - EV_enter_state: Sent to each new State once when it goes onto - the stack. The 'arg' is whatever you define it to be. - - - EV_leave_state: Sent to a state immediately before it is removed - from the stack. - - - EV_reenter_state: If a State gets pushed on top of this one, and - then it pops off, a re-enter Event happens. This should handle - things like consuming the return value of a nested input handler - State. - - Time passing: - - - EV_tick: This happens once per clock tick, which is 16ms or - 62.5Hz by default. The 'arg' is the number of ticks since - entering the state. When 'arg' exceeds 65535, it wraps around - to 32768. - - - EV_sleep_tick: This happens every 0.5s during standby, if - enabled at compile time. The 'arg' is the number of ticks since - entering the state. When 'arg' exceeds 65535, it wraps around - to 32768. - - LVP and thermal regulation: - - - EV_voltage_low: Sent whenever the input power drops below the - VOLTAGE_LOW threshold. Minimum of VOLTAGE_WARNING_SECONDS - between events. - - - EV_temperature_high: Sent whenever the MCU's projected temperature - is higher than therm_ceil. Minimum of one second between events. - The 'arg' indicates how far the temperature exceeds the limit. - - - EV_temperature_low: Sent whenever the MCU's projected temperature - is lower than (therm_ceil - THERMAL_WINDOW_SIZE). Minimum of - one second between events. The 'arg' indicates how far the - temperature exceeds the limit. - - Button presses: - - Button events can be referred to either by pre-defined symbols, or - by teasing out the flags manually. The structure of a button - event is as follows: - - - Bit 7: 1 for button events, 0 otherwise. - - - Bit 6: 1 for a "timeout" event (signals the end of a - sequence), or 0 otherwise. - - - Bit 5: 1 for a "hold" event, 0 otherwise. This flag is only - necessary because, without it, it would be impossible to - distinguish between "click, click, timeout" and "click, hold, - release". - - - Bit 4: 1 if button is currently pressed, 0 otherwise. Button - release events look just like button press events, except this - is not set. - - - Bits 0,1,2,3: Counter for how many clicks there have been. - The first click is 1, second is 2, and it goes up to 15 clicks - in a row. Clicks after 15 are coded as 15. - - The pre-defined button event symbols are like the following: - - - EV_click1_press: The user pressed the button, but no time has - passed since then. - - - EV_click1_release: The user pressed and released the button, - but no time has passed since then. - - - EV_click1_complete: The user clicked the e-switch, released - it, and enough time passed that no more clicks were detected. - (a.k.a. EV_1click) - - - EV_click1_hold: The user pressed the button, and continued - holding it long enough to count as a "hold" event. This event - is sent once per timer tick as long as the button is held, and - the 'arg' value indicates how many timer ticks since the - button state went from 'press' to 'hold'. - - - EV_click1_hold_release: The button was released at the end of - a "hold" event. This is the end of the input sequence, - because no timeout period is used after a hold. - - It's worth noting that a "hold" event can only happen at the - end of an input sequence, and the sequence will reset to empty - after the hold is released. - - If the user pressed the button more than once, events follow the - same pattern. These are the same as above, except with a full - short-press and release first. - - - EV_click2_press - - EV_click2_release - - EV_click2_complete (a.k.a. EV_2clicks) - - EV_click2_hold - - EV_click2_hold_release - - Each of the above patterns continues up to 15 clicks. - - To match entire categories of events, use the bitmasks provided. - For example, to match button events where the button is down or - the button is up, the code would look like this: - - if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { - // button is down (can be a press event or a hold event) - } - else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { - // button was just released - } - - In theory, you could also define your own arbitrary event types, and - emit() them as necessary, and handle them in State functions the same - as any other event. - - -Cooperative multitasking: - - Since we don't have true preemptive multitasking, the best we can do - is cooperative multitasking. In practice, this means: - - - Declare global variables as volatile if they can be changed by an - event handler. This keeps the compiler from caching the value and - causing incorrect behavior. - - - Don't put long-running tasks into State functions. Each State - will get called at least once every 16ms for a clock tick, so they - should not run for longer than 16ms. - - - Put long-running tasks into loop() instead. - - - For long delay() calls, use nice_delay_ms(). This allows the MCU - to process events while we wait. It also automatically aborts if - it detects a state change, and returns a different value. - - In many cases, it shouldn't be necessary to do anything more than - this, but sometimes it will also be a good idea to check the - return value and abort the current task: - - if (! nice_delay_ms(mydelay)) break; - - - In general, try to do small amounts of work and then return - control to other parts of the program. Keep doing small amounts - and yielding until a task is done, instead of trying to do it all - at once. - - -Persistent data in EEPROM: - - To save data which lasts after a battery change, use the eeprom - functions. Define an eeprom style (or two) at the top, define how - many bytes to allocate, and then use the relevant functions as - appropriate. - - - USE_EEPROM / USE_EEPROM_WL: Enable the eeprom-related functions. - With "WL", it uses wear-levelling. Without, it does not. Note: - Wear levelling is not necessarily better -- it uses more ROM, and - it writes more bytes per save(). So, use it only for a few bytes - which change frequently -- not for many bytes or infrequent - changes. - - - EEPROM_BYTES N / EEPROM_WL_BYTES N: Allocate N bytes for the - eeprom data. - - - load_eeprom() / load_eeprom_wl(): Load the stored data into the - eeprom[] or eeprom_wl[] arrays. - Returns 1 if data was found, 0 otherwise. - - - save_eeprom() / save_eeprom_wl(): Save the eeprom[] or eeprom_wl[] - array data to persistent storage. The WL version erases all old - values and writes new ones in a different part of the eeprom - space. The non-WL version updates values in place, and does not - overwrite values which didn't change. - - Note that all interrupts will be disabled during eeprom operations. - - -Useful #defines: - - A variety of things can be #defined before including - spaghetti-monster.h in your program. This allows you to tweak the - behavior and set options to fit your needs: - - - FSM_something_LAYOUT: Select a driver type from tk-attiny.h. This - controls how many power channels there are, which pins they're on, - and what other driver features are available. - - - USE_LVP: Enable low-voltage protection. - - - VOLTAGE_LOW: What voltage should LVP trigger at? Defaults to 29 (2.9V). - - - VOLTAGE_FUDGE_FACTOR: Add this much to the voltage measurements, - to compensate for voltage drop across the reverse-polarity - diode. - - - VOLTAGE_WARNING_SECONDS: How long to wait between LVP events. - - - USE_THERMAL_REGULATION: Enable thermal regulation - - - DEFAULT_THERM_CEIL: Set the temperature limit to use by default - when the user hasn't configured anything. - - - USE_RAMPING: Enable smooth ramping helpers. - - - RAMP_LENGTH: Pick a pre-defined ramp by length. Defined sizes - are 50, 75, and 150 levels. - - - USE_DELAY_4MS, USE_DELAY_MS, USE_DELAY_ZERO: Enable the delay_4ms, - delay_ms(), and delay_zero() functions. Useful for timing-related - activities. - - - HOLD_TIMEOUT: How many clock ticks before a "press" event becomes - a "hold" event? - - - RELEASE_TIMEOUT: How many clock ticks before a "release" event - becomes a "click" event? Basically, the maximum time between - clicks in a double-click or triple-click. - - - USE_BATTCHECK: Enable the battcheck function. Also define one of - the following to select a display style: - - - BATTCHECK_VpT: Volts, pause, tenths. - - BATTCHECK_4bars: Blink up to 4 times. - - BATTCHECK_6bars: Blink up to 6 times. - - BATTCHECK_8bars: Blink up to 8 times. - - - ... and many others. Will try to document them over time, but - they can be found by searching for pretty much anything in - all-caps in the fsm-*.[ch] files. diff --git a/spaghetti-monster/werner/Makefile b/spaghetti-monster/werner/Makefile deleted file mode 100644 index 21d85f7..0000000 --- a/spaghetti-monster/werner/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -all: - ./build-all.sh - -clean: - rm -f cfg-*.h *.hex *~ *.elf *.o - -.phony: clean diff --git a/spaghetti-monster/werner/README b/spaghetti-monster/werner/README deleted file mode 100644 index 5fe392d..0000000 --- a/spaghetti-monster/werner/README +++ /dev/null @@ -1,56 +0,0 @@ -This is a Werner-style interface for dual-switch lights -(e-switch + clicky switch). What that means is: - -While the clicky switch is off: - - - Click the clicky switch: Turn on, at the last-used level. The clicky - switch works as a momentary mode. - - - Click the clicky switch while holding the e-switch: Go into sort of a - utility mode. - -While on, in a normal steady mode: - - - Click the clicky switch: Turn off. - - - Click the e-switch: Brighter. One step per click. - - - Hold the e-switch: Dimmer. Keep holding to go down multiple steps. - -While in standby, in utility mode: - - - Click the e-switch: Turn on. - - - Hold the e-switch: Turn on at lowest level. - - - Double-click the e-switch: Turn on at highest level. - - - Triple-click the e-switch: Battery check mode. - - - Quad-click the e-switch: Ramp config mode. - -While in battery check mode: - - - Click either switch: Turn off. - - - Double-click the e-switch: Go to temperature check mode. - -While in temperature check mode: - - - Click either switch: Turn off. - - - Double-click the e-switch: Go to battery check mode. - - - Quad-click the e-switch: Go to thermal config mode. - -Ramp config mode and thermal config mode work the same as in Anduril or -RampingIOS V3. The options are: - - - Ramp config mode: - 1. Floor level. - 2. Ceiling level. - 3. Number of steps. - - - Thermal config mode: - 1. Calibrate sensor by entering current temperature in C. - 2. Set temperature limit to 30 C + N clicks. diff --git a/spaghetti-monster/werner/build-all.sh b/spaghetti-monster/werner/build-all.sh deleted file mode 100755 index b114101..0000000 --- a/spaghetti-monster/werner/build-all.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh - -cp -av ../anduril/cfg*.h . - -UI=werner - -for TARGET in cfg-*.h ; do - NAME=$(echo "$TARGET" | perl -ne '/cfg-(.*).h/ && print "$1\n";') - echo "===== $NAME =====" - ATTINY=$(grep 'ATTINY:' $TARGET | awk '{ print $3 }') - if [ -z "$ATTINY" ]; then ATTINY=85 ; fi - echo ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}" - ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}" - mv -f "$UI".hex "$UI".$NAME.hex -done diff --git a/spaghetti-monster/werner/werner.c b/spaghetti-monster/werner/werner.c deleted file mode 100644 index f3241ee..0000000 --- a/spaghetti-monster/werner/werner.c +++ /dev/null @@ -1,715 +0,0 @@ -/* - * Werner: Werner-style dual-switch UI for SpaghettiMonster. - * Side click to go up, side hold to go down, tail click for on/off. - * - * Copyright (C) 2018 Selene ToyKeeper - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -/********* User-configurable options *********/ -// Physical driver type (uncomment one of the following or define it at the gcc command line) -//#define CONFIGFILE cfg-emisar-d4.h - -#define USE_LVP // FIXME: won't build when this option is turned off - -// parameters for this defined below or per-driver -#define USE_THERMAL_REGULATION -#define DEFAULT_THERM_CEIL 45 // try not to get hotter than this - -// battery readout style (pick one) -#define BATTCHECK_VpT -//#define BATTCHECK_8bars // FIXME: breaks build -//#define BATTCHECK_4bars // FIXME: breaks build - -// cut clock speed at very low modes for better efficiency -// (defined here so config files can override it) -#define USE_DYNAMIC_UNDERCLOCKING - -/***** specific settings for known driver types *****/ -#ifdef CONFIGFILE -#include "tk.h" -#include incfile(CONFIGFILE) -#else -#error You need to define CONFIGFILE -#endif - -// thermal properties, if not defined per-driver -#ifndef MIN_THERM_STEPDOWN -#define MIN_THERM_STEPDOWN MAX_1x7135 // lowest value it'll step down to -#endif -#ifndef THERM_FASTER_LEVEL - #ifdef MAX_Nx7135 - #define THERM_FASTER_LEVEL MAX_Nx7135 // throttle back faster when high - #else - #define THERM_FASTER_LEVEL (RAMP_SIZE*4/5) // throttle back faster when high - #endif -#endif -#ifdef USE_THERMAL_REGULATION -#define USE_SET_LEVEL_GRADUALLY // isn't used except for thermal adjustments -#endif - - -/********* Configure SpaghettiMonster *********/ -#define USE_DELAY_ZERO -#define USE_RAMPING -#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file -#define USE_BATTCHECK -#define USE_IDLE_MODE // reduce power use while awake and no tasks are pending - -// auto-detect how many eeprom bytes -#define USE_EEPROM -#ifdef USE_THERMAL_REGULATION -#define EEPROM_BYTES 5 -#else -#define EEPROM_BYTES 3 -#endif -// for mode memory on tail switch -#define USE_EEPROM_WL -#define EEPROM_WL_BYTES 1 - -#include "spaghetti-monster.h" - - -// FSM states -uint8_t off_state(Event event, uint16_t arg); -// simple numeric entry config menu -uint8_t config_state_base(Event event, uint16_t arg, - uint8_t num_config_steps, - void (*savefunc)()); -#define MAX_CONFIG_VALUES 3 -uint8_t config_state_values[MAX_CONFIG_VALUES]; -// ramping mode and its related config mode -uint8_t steady_state(Event event, uint16_t arg); -uint8_t ramp_config_state(Event event, uint16_t arg); -#ifdef USE_BATTCHECK -uint8_t battcheck_state(Event event, uint16_t arg); -#endif -#ifdef USE_THERMAL_REGULATION -uint8_t tempcheck_state(Event event, uint16_t arg); -uint8_t thermal_config_state(Event event, uint16_t arg); -#endif - -// general helper function for config modes -uint8_t number_entry_state(Event event, uint16_t arg); -// return value from number_entry_state() -volatile uint8_t number_entry_value; - -void blink_confirm(uint8_t num); - -// remember stuff even after battery was changed -void load_config(); -void save_config(); -void save_config_wl(); - -// default ramp options if not overridden earlier per-driver -#ifndef RAMP_DISCRETE_FLOOR - #define RAMP_DISCRETE_FLOOR 1 -#endif -#ifndef RAMP_DISCRETE_CEIL - #define RAMP_DISCRETE_CEIL RAMP_SIZE -#endif -#ifndef RAMP_DISCRETE_STEPS - #define RAMP_DISCRETE_STEPS 7 -#endif - -// brightness control -uint8_t memorized_level = MAX_1x7135; -// smooth vs discrete ramping -volatile uint8_t ramp_discrete_floor = RAMP_DISCRETE_FLOOR; -volatile uint8_t ramp_discrete_ceil = RAMP_DISCRETE_CEIL; -volatile uint8_t ramp_discrete_steps = RAMP_DISCRETE_STEPS; -uint8_t ramp_discrete_step_size; // don't set this - -// calculate the nearest ramp level which would be valid at the moment -// (is a no-op for smooth ramp, but limits discrete ramp to only the -// correct levels for the user's config) -uint8_t nearest_level(int16_t target); - -#ifdef USE_THERMAL_REGULATION -// brightness before thermal step-down -uint8_t target_level = 0; -#endif - - -uint8_t off_state(Event event, uint16_t arg) { - // turn emitter off when entering state - if ((event == EV_enter_state) || (event == EV_reenter_state)) { - // let the user know the power is connected - blink_confirm(1); - // but otherwise stay off - set_level(0); - // sleep while off (lower power use) - go_to_standby = 1; - return EVENT_HANDLED; - } - // go back to sleep eventually if we got bumped but didn't leave "off" state - else if (event == EV_tick) { - if (arg > TICKS_PER_SECOND*2) { - go_to_standby = 1; - } - return EVENT_HANDLED; - } - // hold (initially): go to lowest level, but allow abort for regular click - else if (event == EV_click1_press) { - set_level(nearest_level(1)); - return EVENT_HANDLED; - } - // hold: go to lowest level - else if (event == EV_click1_hold) { - // don't start ramping immediately; - // give the user time to release at moon level - if (arg >= HOLD_TIMEOUT) { - set_state(steady_state, 1); - } - return EVENT_HANDLED; - } - // hold, release quickly: go to lowest level - else if (event == EV_click1_hold_release) { - set_state(steady_state, 1); - return EVENT_HANDLED; - } - // 1 click (before timeout): go to memorized level, but allow abort for double click - else if (event == EV_click1_release) { - set_level(nearest_level(memorized_level)); - return EVENT_HANDLED; - } - // 1 click: regular mode - else if (event == EV_1click) { - set_state(steady_state, memorized_level); - return EVENT_HANDLED; - } - // 2 clicks (initial press): off, to prep for later events - else if (event == EV_click2_press) { - set_level(0); - return EVENT_HANDLED; - } - // click, hold: go to highest level (for ramping down) - else if (event == EV_click2_hold) { - set_state(steady_state, MAX_LEVEL); - return EVENT_HANDLED; - } - // 2 clicks: highest mode - else if (event == EV_2clicks) { - set_state(steady_state, nearest_level(MAX_LEVEL)); - return EVENT_HANDLED; - } - #ifdef USE_BATTCHECK - // 3 clicks: battcheck mode / blinky mode group - else if (event == EV_3clicks) { - set_state(battcheck_state, 0); - return EVENT_HANDLED; - } - #endif - // 4 clicks: configure ramp - else if (event == EV_4clicks) { - push_state(ramp_config_state, 0); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - - -uint8_t steady_state(Event event, uint16_t arg) { - uint8_t mode_min = ramp_discrete_floor; - uint8_t mode_max = ramp_discrete_ceil; - uint8_t ramp_step_size = ramp_discrete_step_size; - - // turn LED on when we first enter the mode - if ((event == EV_enter_state) || (event == EV_reenter_state)) { - // if we just got back from config mode, go back to memorized level - if (event == EV_reenter_state) { - arg = memorized_level; - } - // remember this level, unless it's moon or turbo - if ((arg > mode_min) && (arg < mode_max)) - memorized_level = arg; - // use the requested level even if not memorized - #ifdef USE_THERMAL_REGULATION - target_level = arg; - #endif - set_level(nearest_level(arg)); - return EVENT_HANDLED; - } - // click: brighter - else if (event == EV_click1_release) { - memorized_level = nearest_level((int16_t)actual_level + ramp_step_size); - #ifdef USE_THERMAL_REGULATION - target_level = memorized_level; - #endif - set_level(memorized_level); - // make sure next click will respond quickly - empty_event_sequence(); - // remember mode for later - save_config_wl(); - return EVENT_HANDLED; - } - // hold: dimmer - else if (event == EV_click1_hold) { - // ramp slower in discrete mode - if (arg % HOLD_TIMEOUT != 0) { - return EVENT_HANDLED; - } - memorized_level = nearest_level((int16_t)actual_level - ramp_step_size); - #ifdef USE_THERMAL_REGULATION - target_level = memorized_level; - #endif - set_level(memorized_level); - return EVENT_HANDLED; - } - // reverse ramp direction on hold release - else if (event == EV_click1_hold_release) { - save_config_wl(); - return EVENT_HANDLED; - } - #if defined(USE_SET_LEVEL_GRADUALLY) - // gradual thermal regulation - else if (event == EV_tick) { - #ifdef USE_SET_LEVEL_GRADUALLY - // make thermal adjustment speed scale with magnitude - if ((arg & 1) && (actual_level < THERM_FASTER_LEVEL)) { - return EVENT_HANDLED; // adjust slower when not a high mode - } - #ifdef THERM_HARD_TURBO_DROP - else if ((! (actual_level < THERM_FASTER_LEVEL)) - && (actual_level > gradual_target)) { - gradual_tick(); - } - else { - #endif - // [int(62*4 / (x**0.95)) for x in (1,2,4,8,16,32,64,128)] - uint8_t intervals[] = {248, 128, 66, 34, 17, 9, 4, 2}; - uint8_t diff; - static uint8_t ticks_since_adjust = 0; - ticks_since_adjust ++; - if (gradual_target > actual_level) diff = gradual_target - actual_level; - else { - diff = actual_level - gradual_target; - } - uint8_t magnitude = 0; - #ifndef THERM_HARD_TURBO_DROP - // if we're on a really high mode, drop faster - if (actual_level >= THERM_FASTER_LEVEL) { magnitude ++; } - #endif - while (diff) { - magnitude ++; - diff >>= 1; - } - uint8_t ticks_per_adjust = intervals[magnitude]; - if (ticks_since_adjust > ticks_per_adjust) - { - gradual_tick(); - ticks_since_adjust = 0; - } - //if (!(arg % ticks_per_adjust)) gradual_tick(); - #ifdef THERM_HARD_TURBO_DROP - } - #endif - #endif - return EVENT_HANDLED; - } - #endif - #ifdef USE_THERMAL_REGULATION - // overheating: drop by an amount proportional to how far we are above the ceiling - else if (event == EV_temperature_high) { - #ifdef THERM_HARD_TURBO_DROP - if (actual_level > THERM_FASTER_LEVEL) { - #ifdef USE_SET_LEVEL_GRADUALLY - set_level_gradually(THERM_FASTER_LEVEL); - #else - set_level(THERM_FASTER_LEVEL); - #endif - } else - #endif - if (actual_level > MIN_THERM_STEPDOWN) { - int16_t stepdown = actual_level - arg; - if (stepdown < MIN_THERM_STEPDOWN) stepdown = MIN_THERM_STEPDOWN; - else if (stepdown > MAX_LEVEL) stepdown = MAX_LEVEL; - #ifdef USE_SET_LEVEL_GRADUALLY - set_level_gradually(stepdown); - #else - set_level(stepdown); - #endif - } - return EVENT_HANDLED; - } - // underheating: increase slowly if we're lower than the target - // (proportional to how low we are) - else if (event == EV_temperature_low) { - if (actual_level < target_level) { - //int16_t stepup = actual_level + (arg>>1); - int16_t stepup = actual_level + arg; - if (stepup > target_level) stepup = target_level; - else if (stepup < MIN_THERM_STEPDOWN) stepup = MIN_THERM_STEPDOWN; - #ifdef USE_SET_LEVEL_GRADUALLY - set_level_gradually(stepup); - #else - set_level(stepup); - #endif - } - return EVENT_HANDLED; - } - #endif - return EVENT_NOT_HANDLED; -} - - -#ifdef USE_BATTCHECK -uint8_t battcheck_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - #ifdef USE_THERMAL_REGULATION - // 2 clicks: tempcheck mode - else if (event == EV_2clicks) { - blink_confirm(2); - set_state(tempcheck_state, 0); - return EVENT_HANDLED; - } - #endif - return EVENT_NOT_HANDLED; -} -#endif - -#ifdef USE_THERMAL_REGULATION -uint8_t tempcheck_state(Event event, uint16_t arg) { - // 1 click: off - if (event == EV_1click) { - set_state(off_state, 0); - return EVENT_HANDLED; - } - // 2 clicks: battcheck mode - else if (event == EV_2clicks) { - blink_confirm(1); - set_state(battcheck_state, 0); - return EVENT_HANDLED; - } - // 4 clicks: thermal config mode - else if (event == EV_4clicks) { - push_state(thermal_config_state, 0); - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} -#endif - - -// ask the user for a sequence of numbers, then save them and return to caller -uint8_t config_state_base(Event event, uint16_t arg, - uint8_t num_config_steps, - void (*savefunc)()) { - static uint8_t config_step; - if (event == EV_enter_state) { - config_step = 0; - set_level(0); - return EVENT_HANDLED; - } - // advance forward through config steps - else if (event == EV_tick) { - if (config_step < num_config_steps) { - push_state(number_entry_state, config_step + 1); - } - else { - // TODO: blink out some sort of success pattern - savefunc(); - save_config(); - //set_state(retstate, retval); - pop_state(); - } - return EVENT_HANDLED; - } - // an option was set (return from number_entry_state) - else if (event == EV_reenter_state) { - config_state_values[config_step] = number_entry_value; - config_step ++; - return EVENT_HANDLED; - } - //return EVENT_NOT_HANDLED; - // eat all other events; don't pass any through to parent - return EVENT_HANDLED; -} - -void ramp_config_save() { - // parse values - uint8_t val; - - val = config_state_values[0]; - if (val) { ramp_discrete_floor = val; } - - val = config_state_values[1]; - if (val) { ramp_discrete_ceil = MAX_LEVEL + 1 - val; } - - val = config_state_values[2]; - if (val) ramp_discrete_steps = val; -} - -uint8_t ramp_config_state(Event event, uint16_t arg) { - uint8_t num_config_steps; - num_config_steps = 3; - return config_state_base(event, arg, - num_config_steps, ramp_config_save); -} - - -#ifdef USE_THERMAL_REGULATION -void thermal_config_save() { - // parse values - uint8_t val; - - // calibrate room temperature - val = config_state_values[0]; - if (val) { - int8_t rawtemp = temperature - therm_cal_offset; - therm_cal_offset = val - rawtemp; - reset_thermal_history = 1; // invalidate all recent temperature data - } - - val = config_state_values[1]; - if (val) { - // set maximum heat limit - therm_ceil = 30 + val - 1; - } - if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL; -} - -uint8_t thermal_config_state(Event event, uint16_t arg) { - return config_state_base(event, arg, - 2, thermal_config_save); -} -#endif - - -uint8_t number_entry_state(Event event, uint16_t arg) { - static uint8_t value; - static uint8_t blinks_left; - static uint8_t entry_step; - static uint16_t wait_ticks; - if (event == EV_enter_state) { - value = 0; - blinks_left = arg; - entry_step = 0; - wait_ticks = 0; - return EVENT_HANDLED; - } - // advance through the process: - // 0: wait a moment - // 1: blink out the 'arg' value - // 2: wait a moment - // 3: "buzz" while counting clicks - // 4: save and exit - else if (event == EV_tick) { - // wait a moment - if ((entry_step == 0) || (entry_step == 2)) { - if (wait_ticks < TICKS_PER_SECOND/2) - wait_ticks ++; - else { - entry_step ++; - wait_ticks = 0; - } - } - // blink out the option number - else if (entry_step == 1) { - if (blinks_left) { - if ((wait_ticks & 31) == 10) { - set_level(RAMP_SIZE/4); - } - else if ((wait_ticks & 31) == 20) { - set_level(0); - } - else if ((wait_ticks & 31) == 31) { - blinks_left --; - } - wait_ticks ++; - } - else { - entry_step ++; - wait_ticks = 0; - } - } - else if (entry_step == 3) { // buzz while waiting for a number to be entered - wait_ticks ++; - // buzz for N seconds after last event - if ((wait_ticks & 3) == 0) { - set_level(RAMP_SIZE/6); - } - else if ((wait_ticks & 3) == 2) { - set_level(RAMP_SIZE/8); - } - // time out after 3 seconds - if (wait_ticks > TICKS_PER_SECOND*3) { - //number_entry_value = value; - set_level(0); - entry_step ++; - } - } - else if (entry_step == 4) { - number_entry_value = value; - pop_state(); - } - return EVENT_HANDLED; - } - // count clicks - else if (event == EV_click1_release) { - empty_event_sequence(); - if (entry_step == 3) { // only count during the "buzz" - value ++; - wait_ticks = 0; - // flash briefly - set_level(RAMP_SIZE/2); - delay_4ms(8/2); - set_level(0); - } - return EVENT_HANDLED; - } - return EVENT_NOT_HANDLED; -} - - -// find the ramp level closest to the target, -// using only the levels which are allowed in the current state -uint8_t nearest_level(int16_t target) { - // bounds check - // using int16_t here saves us a bunch of logic elsewhere, - // by allowing us to correct for numbers < 0 or > 255 in one central place - uint8_t mode_min = ramp_discrete_floor; - uint8_t mode_max = ramp_discrete_ceil; - if (target < mode_min) return mode_min; - if (target > mode_max) return mode_max; - - uint8_t ramp_range = ramp_discrete_ceil - ramp_discrete_floor; - ramp_discrete_step_size = ramp_range / (ramp_discrete_steps-1); - uint8_t this_level = ramp_discrete_floor; - - for(uint8_t i=0; i>1)) - return this_level; - } - return this_level; -} - - -void blink_confirm(uint8_t num) { - for (; num>0; num--) { - set_level(MAX_LEVEL/4); - delay_4ms(10/4); - set_level(0); - delay_4ms(100/4); - } -} - - -void load_config() { - if (load_eeprom()) { - ramp_discrete_floor = eeprom[0]; - ramp_discrete_ceil = eeprom[1]; - ramp_discrete_steps = eeprom[2]; - #ifdef USE_THERMAL_REGULATION - therm_ceil = eeprom[3]; - therm_cal_offset = eeprom[4]; - #endif - } - if (load_eeprom_wl()) { - memorized_level = eeprom_wl[0]; - } -} - - -void save_config() { - eeprom[0] = ramp_discrete_floor; - eeprom[1] = ramp_discrete_ceil; - eeprom[2] = ramp_discrete_steps; - #ifdef USE_THERMAL_REGULATION - eeprom[3] = therm_ceil; - eeprom[4] = therm_cal_offset; - #endif - - save_eeprom(); -} - - -void save_config_wl() { - eeprom_wl[0] = memorized_level; - save_eeprom_wl(); -} - - -void low_voltage() { - StatePtr state = current_state; - - // in normal mode, step down or turn off - if (state == steady_state) { - if (actual_level > 1) { - uint8_t lvl = (actual_level >> 1) + (actual_level >> 2); - set_level(lvl); - #ifdef USE_THERMAL_REGULATION - target_level = lvl; - #endif - } - else { - set_state(off_state, 0); - } - } - // all other modes, just turn off when voltage is low - else { - set_state(off_state, 0); - } -} - - -void setup() { - // dual switch: e-switch + power clicky - // power clicky acts as a momentary mode - load_config(); - - if (button_is_pressed()) - // hold button to go to moon - push_state(off_state, 0); - else - // otherwise use memory - push_state(steady_state, memorized_level); -} - - -void loop() { - - StatePtr state = current_state; - - if (0) {} - - #ifdef USE_BATTCHECK - else if (state == battcheck_state) { - battcheck(); - } - #endif - #ifdef USE_THERMAL_REGULATION - // TODO: blink out therm_ceil during thermal_config_state - else if (state == tempcheck_state) { - blink_num(temperature); - nice_delay_ms(1000); - } - #endif - - #ifdef USE_IDLE_MODE - else { - // doze until next clock tick - idle_mode(); - } - #endif - -} diff --git a/ui/anduril/Makefile b/ui/anduril/Makefile new file mode 100644 index 0000000..332f0f3 --- /dev/null +++ b/ui/anduril/Makefile @@ -0,0 +1,14 @@ +all: + ./build-all.sh + +clean: + rm -f *.hex *~ *.elf *.o + +todo: + @egrep 'TODO:|FIXME:' *.[ch] + +models: + @./models.py > MODELS + @cat MODELS + +.phony: clean todo diff --git a/ui/anduril/anduril.c b/ui/anduril/anduril.c new file mode 100644 index 0000000..e46eeaf --- /dev/null +++ b/ui/anduril/anduril.c @@ -0,0 +1,396 @@ +// Anduril: Narsil-inspired UI for SpaghettiMonster. +// (Anduril is Aragorn's sword, the blade Narsil reforged) +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +/* + * Usually a program would be structured like this... + * - Library headers + * - App headers + * - App code + * + * ... in each source file. + * ... and each library and part of the program would be linked together. + * + * But this doesn't follow that pattern, because it's using the + * -fwhole-program + * flag to reduce the compiled size. It lets us fit more features + * in a tiny MCU chip's ROM. + * + * So the structure is like this instead... + * - App-level configuration headers + * - Default config + * - Per build target config + * - Library-level configuration headers + * - Library code (FSM itself) + * - App headers + * - App code (all of it, inline) + * + * Don't do this in regular programs. It's weird and kind of gross. + * But in this case it gives us a bunch of much-needed space, so... woot. + * + * Also, there are a ton of compile-time options because it needs to build + * a bunch of different versions and each one needs to be trimmed as small + * as possible. These are mostly "USE" flags. + */ + +/********* User-configurable options *********/ +#include "config-default.h" + +/********* specific settings for known driver types *********/ +// Anduril config file name (set it here or define it at the gcc command line) +//#define CFG_H cfg-blf-q8.h + +#include "tk.h" +#include incfile(CFG_H) + + +/********* Include headers which need to be before FSM *********/ + +// enable FSM features needed by basic ramping functions +#include "ramp-mode-fsm.h" + +#ifdef USE_FACTORY_RESET +#include "factory-reset-fsm.h" +#endif + +#ifdef USE_BATTCHECK_MODE +#include "battcheck-mode-fsm.h" +#endif + +#ifdef USE_LOCKOUT_MODE +#include "lockout-mode-fsm.h" +#endif + +// enable FSM features needed by strobe modes +#include "strobe-modes-fsm.h" + +// figure out how many bytes of eeprom are needed, +// based on which UI features are enabled +// (include this one last) +#include "load-save-config-fsm.h" + + +/********* bring in FSM / SpaghettiMonster *********/ +#define USE_IDLE_MODE // reduce power use while awake and no tasks are pending + +#include "spaghetti-monster.h" + +/********* does this build target have special code to include? *********/ +#ifdef HWDEF_C_FILE +#include incfile(HWDEF_C_FILE) +#endif +#ifdef CFG_C_FILE +#include incfile(CFG_C_FILE) +#endif + + +/********* Include all the regular app headers *********/ + +#include "off-mode.h" +#include "ramp-mode.h" +#include "config-mode.h" +#include "aux-leds.h" +#include "misc.h" + +#ifdef USE_SUNSET_TIMER +#include "sunset-timer.h" +#endif + +#ifdef USE_VERSION_CHECK +#include "version-check-mode.h" +#endif + +#ifdef USE_BATTCHECK_MODE +#include "battcheck-mode.h" +#endif + +#ifdef USE_BEACON_MODE +#include "beacon-mode.h" +#endif + +#ifdef USE_THERMAL_REGULATION +#include "tempcheck-mode.h" +#endif + +#ifdef USE_LOCKOUT_MODE +#include "lockout-mode.h" +#endif + +#ifdef USE_MOMENTARY_MODE +#include "momentary-mode.h" +#endif + +#ifdef USE_TACTICAL_MODE +#include "tactical-mode.h" +#endif + +// allow the channel mode handler even when only 1 mode +// (so a tint ramp light could still use 3H even if there's no other mode) +#if defined(USE_CHANNEL_MODES) +#include "channel-modes.h" +#endif + +#ifdef USE_FACTORY_RESET +#include "factory-reset.h" +#endif + +// this one detects its own enable/disable settings +#include "strobe-modes.h" + +#ifdef USE_SOS_MODE +#include "sos-mode.h" +#endif + +#ifdef USE_SMOOTH_STEPS +#include "smooth-steps.h" +#endif + +// this should be last, so other headers have a chance to declare values +#include "load-save-config.h" + + +/********* Include all the app logic source files *********/ +// (is a bit weird to do things this way, +// but it saves a lot of space by letting us use the -fwhole-program flag) + +#include "off-mode.c" +#include "ramp-mode.c" +#include "load-save-config.c" +#include "config-mode.c" +#include "aux-leds.c" +#include "misc.c" + +#ifdef USE_SUNSET_TIMER +#include "sunset-timer.c" +#endif + +#ifdef USE_VERSION_CHECK +#include "version-check-mode.c" +#endif + +#ifdef USE_BATTCHECK_MODE +#include "battcheck-mode.c" +#endif + +#ifdef USE_BEACON_MODE +#include "beacon-mode.c" +#endif + +#ifdef USE_THERMAL_REGULATION +#include "tempcheck-mode.c" +#endif + +#ifdef USE_LOCKOUT_MODE +#include "lockout-mode.c" +#endif + +#ifdef USE_MOMENTARY_MODE +#include "momentary-mode.c" +#endif + +#ifdef USE_TACTICAL_MODE +#include "tactical-mode.c" +#endif + +#if defined(USE_CHANNEL_MODES) +#include "channel-modes.c" +#endif + +#ifdef USE_FACTORY_RESET +#include "factory-reset.c" +#endif + +#ifdef USE_STROBE_STATE +#include "strobe-modes.c" +#endif + +#ifdef USE_SOS_MODE +#include "sos-mode.c" +#endif + +#ifdef USE_SMOOTH_STEPS +#include "smooth-steps.c" +#endif + + +// runs one time at boot, when power is connected +void setup() { + + #ifndef START_AT_MEMORIZED_LEVEL + + // regular e-switch light, no hard clicky power button + + // blink at power-on to let user know power is connected + blink_once(); + + #ifdef USE_FACTORY_RESET + if (button_is_pressed()) + factory_reset(); + #endif + + load_config(); + + #if defined(USE_MANUAL_MEMORY) && defined(USE_MANUAL_MEMORY_TIMER) + // without this, initial boot-up brightness is wrong + // when manual mem is enabled with a non-zero timer + if (cfg.manual_memory) manual_memory_restore(); + #endif + + #if defined(USE_CHANNEL_MODES) + // add channel mode functions underneath every other state + push_state(channel_mode_state, 0); + #endif + + push_state(off_state, 1); + + #else // if START_AT_MEMORIZED_LEVEL + + // dual switch: e-switch + power clicky + // power clicky acts as a momentary mode + load_config(); + + #if defined(USE_CHANNEL_MODES) + // add channel mode functions underneath every other state + push_state(channel_mode_state, 0); + #endif + + if (button_is_pressed()) + // hold button to go to moon + push_state(steady_state, 1); + else + // otherwise use memory + push_state(steady_state, memorized_level); + + #endif // ifdef START_AT_MEMORIZED_LEVEL + +} + + +// runs repeatedly whenever light is "on" (not in standby) +void loop() { + + // "current_state" is volatile, so cache it to reduce code size + StatePtr state = current_state; + + #ifdef USE_AUX_RGB_LEDS_WHILE_ON + // display battery charge on RGB button during use + if (state == steady_state) + rgb_led_voltage_readout(actual_level > USE_AUX_RGB_LEDS_WHILE_ON); + #endif + + if (0) {} // placeholder + + #ifdef USE_VERSION_CHECK + else if (state == version_check_state) { + version_check_iter(); + } + #endif + + #ifdef USE_STROBE_STATE + else if ((state == strobe_state) + #ifdef USE_MOMENTARY_MODE + // also handle momentary strobes + || (( + (state == momentary_state) + #ifdef USE_TACTICAL_MODE + || (state == tactical_state) + #endif + ) + && (momentary_mode == 1) && (momentary_active)) + #endif + ) { + strobe_state_iter(); + } + #endif // #ifdef USE_STROBE_STATE + + #ifdef USE_BORING_STROBE_STATE + else if (state == boring_strobe_state) { + boring_strobe_state_iter(); + } + #endif + + #ifdef USE_BATTCHECK + else if (state == battcheck_state) { + battcheck(); + #ifdef USE_SIMPLE_UI + // in simple mode, turn off after one readout + // FIXME: can eat the next button press + // (state changes in loop() act weird) + if (cfg.simple_ui_active) set_state_deferred(off_state, 0); + else nice_delay_ms(1000); + #endif + } + #endif + + #ifdef USE_THERMAL_REGULATION + // TODO: blink out therm_ceil during thermal_config_state? + else if (state == tempcheck_state) { + blink_num(temperature); + nice_delay_ms(1000); + } + #endif + + #ifdef USE_BEACON_MODE + else if (state == beacon_state) { + beacon_mode_iter(); + } + #endif + + #if defined(USE_SOS_MODE) && defined(USE_SOS_MODE_IN_BLINKY_GROUP) + else if (state == sos_state) { + sos_mode_iter(); + } + #endif + + #ifdef USE_SMOOTH_STEPS + else if (cfg.smooth_steps_style && smooth_steps_in_progress) { + smooth_steps_iter(); + } + #endif + + #ifdef USE_IDLE_MODE + else { + // doze until next clock tick + idle_mode(); + } + #endif + +} + + +// instead of handling EV_low_voltage in each mode, +// it's handled globally here to make the code smaller and simpler +void low_voltage() { + + // "current_state" is volatile, so cache it to reduce code size + StatePtr state = current_state; + + // TODO: turn off aux LED(s) when power is really low + + if (0) {} // placeholder + + #ifdef USE_STROBE_STATE + // "step down" from strobe to something low + else if (state == strobe_state) { + set_state(steady_state, RAMP_SIZE/6); + } + #endif + + // in normal mode, step down or turn off + else if (state == steady_state) { + if (actual_level > 1) { + uint8_t lvl = (actual_level >> 1) + (actual_level >> 2); + set_level_and_therm_target(lvl); + } + else { + set_state(off_state, 0); + } + } + // all other modes, just turn off when voltage is low + else { + set_state(off_state, 0); + } + +} + diff --git a/ui/anduril/aux-leds.c b/ui/anduril/aux-leds.c new file mode 100644 index 0000000..af59aa6 --- /dev/null +++ b/ui/anduril/aux-leds.c @@ -0,0 +1,210 @@ +// aux-leds.c: Aux LED functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "aux-leds.h" + + +#if defined(USE_INDICATOR_LED) +void indicator_led_update(uint8_t mode, uint8_t tick) { + //uint8_t volts = voltage; // save a few bytes by caching volatile value + // turn off when battery is too low + #ifdef DUAL_VOLTAGE_FLOOR + if (((voltage < VOLTAGE_LOW) && (voltage > DUAL_VOLTAGE_FLOOR)) + || (voltage < DUAL_VOLTAGE_LOW_LOW)) { + #else + if (voltage < VOLTAGE_LOW) { + #endif + indicator_led(0); + } + //#ifdef USE_INDICATOR_LOW_BAT_WARNING + #ifndef DUAL_VOLTAGE_FLOOR // this isn't set up for dual-voltage lights like the Sofirn SP10 Pro + // fast blink a warning when battery is low but not critical + else if (voltage < VOLTAGE_RED) { + indicator_led(mode & (((tick & 0b0010)>>1) - 3)); + } + #endif + //#endif + // normal steady output, 0/1/2 = off / low / high + else if ((mode & 0b00001111) < 3) { + indicator_led(mode); + } + // beacon-like blinky mode + else { + #ifdef USE_OLD_BLINKING_INDICATOR + + // basic blink, 1/8th duty cycle + if (! (tick & 7)) { + indicator_led(2); + } + else { + indicator_led(0); + } + + #else + + // fancy blink, set off/low/high levels here: + static const uint8_t seq[] = {0, 1, 2, 1, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0}; + indicator_led(seq[tick & 15]); + + #endif // ifdef USE_OLD_BLINKING_INDICATOR + } +} +#endif + +#if defined(USE_AUX_RGB_LEDS) && defined(TICK_DURING_STANDBY) +uint8_t voltage_to_rgb() { + static const uint8_t levels[] = { + // voltage, color + 0, 0, // black + #ifdef DUAL_VOLTAGE_FLOOR + // AA / NiMH voltages + 9, 1, // R + 10, 2, // R+G + 11, 3, // G + 12, 4, // G+B + 13, 5, // B + 14, 6, // R + B + 15, 7, // R+G+B + 20, 0, // black + #endif + // li-ion voltages + 29, 1, // R + 33, 2, // R+G + 35, 3, // G + 37, 4, // G+B + 39, 5, // B + 41, 6, // R + B + 44, 7, // R+G+B // skip; looks too similar to G+B + 255, 7, // R+G+B + }; + uint8_t volts = voltage; + //if (volts < VOLTAGE_LOW) return 0; + + uint8_t i; + for (i = 0; volts >= levels[i]; i += 2) {} + uint8_t color_num = levels[(i - 2) + 1]; + return pgm_read_byte(rgb_led_colors + color_num); +} + +// do fancy stuff with the RGB aux LEDs +// mode: 0bPPPPCCCC where PPPP is the pattern and CCCC is the color +// arg: time slice number +void rgb_led_update(uint8_t mode, uint16_t arg) { + static uint8_t rainbow = 0; // track state of rainbow mode + static uint8_t frame = 0; // track state of animation mode + + // turn off aux LEDs when battery is empty + // (but if voltage==0, that means we just booted and don't know yet) + uint8_t volts = voltage; // save a few bytes by caching volatile value + #ifdef DUAL_VOLTAGE_FLOOR + if ((volts) && (((voltage < VOLTAGE_LOW) && (voltage > DUAL_VOLTAGE_FLOOR)) || (voltage < DUAL_VOLTAGE_LOW_LOW))) { + #else + if ((volts) && (volts < VOLTAGE_LOW)) { + #endif + rgb_led_set(0); + #ifdef USE_BUTTON_LED + button_led_set(0); + #endif + return; + } + + uint8_t pattern = (mode>>4); // off, low, high, blinking, ... more? + uint8_t color = mode & 0x0f; + + // always preview in high mode + if (setting_rgb_mode_now) { pattern = 2; } + + #ifdef USE_POST_OFF_VOLTAGE + // use voltage high mode for a few seconds after initial poweroff + // (but not after changing aux LED settings and other similar actions) + else if ((arg < (cfg.post_off_voltage * SLEEP_TICKS_PER_SECOND)) + && (ticks_since_on < (cfg.post_off_voltage * SLEEP_TICKS_PER_SECOND)) + && (ticks_since_on > 0) // don't blink red on 1st frame + ) { + // use high mode if regular aux level is high or prev level was high + pattern = 1 + ((2 == pattern) | (prev_level >= POST_OFF_VOLTAGE_BRIGHTNESS)); + // voltage mode + color = RGB_LED_NUM_COLORS - 1; + } + #endif + + const uint8_t *colors = rgb_led_colors + 1; + uint8_t actual_color = 0; + if (color < 7) { // normal color + actual_color = pgm_read_byte(colors + color); + } + else if (color == 7) { // disco + rainbow = (rainbow + 1 + pseudo_rand() % 5) % 6; + actual_color = pgm_read_byte(colors + rainbow); + } + else if (color == 8) { // rainbow + uint8_t speed = 0x03; // awake speed + if (go_to_standby) speed = RGB_RAINBOW_SPEED; // asleep speed + if (0 == (arg & speed)) { + rainbow = (rainbow + 1) % 6; + } + actual_color = pgm_read_byte(colors + rainbow); + } + else { // voltage + // show actual voltage while asleep... + if (go_to_standby) { + actual_color = voltage_to_rgb(); + // choose a color based on battery voltage + //if (volts >= 38) actual_color = pgm_read_byte(colors + 4); + //else if (volts >= 33) actual_color = pgm_read_byte(colors + 2); + //else actual_color = pgm_read_byte(colors + 0); + } + // ... but during preview, cycle colors quickly + else { + actual_color = pgm_read_byte(colors + (((arg>>1) % 3) << 1)); + } + } + + // pick a brightness from the animation sequence + if (pattern == 3) { + // uses an odd length to avoid lining up with rainbow loop + static const uint8_t animation[] = {2, 1, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 1}; + frame = (frame + 1) % sizeof(animation); + pattern = animation[frame]; + } + uint8_t result; + #ifdef USE_BUTTON_LED + uint8_t button_led_result; + #endif + switch (pattern) { + case 0: // off + result = 0; + #ifdef USE_BUTTON_LED + button_led_result = 0; + #endif + break; + case 1: // low + result = actual_color; + #ifdef USE_BUTTON_LED + button_led_result = 1; + #endif + break; + default: // high + result = (actual_color << 1); + #ifdef USE_BUTTON_LED + button_led_result = 2; + #endif + break; + } + rgb_led_set(result); + #ifdef USE_BUTTON_LED + button_led_set(button_led_result); + #endif +} + +void rgb_led_voltage_readout(uint8_t bright) { + uint8_t color = voltage_to_rgb(); + if (bright) color = color << 1; + rgb_led_set(color); +} +#endif + diff --git a/ui/anduril/aux-leds.h b/ui/anduril/aux-leds.h new file mode 100644 index 0000000..fa97e6b --- /dev/null +++ b/ui/anduril/aux-leds.h @@ -0,0 +1,65 @@ +// aux-leds.h: Aux LED functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY) +void indicator_led_update(uint8_t mode, uint8_t tick); +#endif +#if defined(USE_AUX_RGB_LEDS) && defined(TICK_DURING_STANDBY) +uint8_t setting_rgb_mode_now = 0; +void rgb_led_update(uint8_t mode, uint16_t arg); +void rgb_led_voltage_readout(uint8_t bright); +/* + * 0: R + * 1: RG + * 2: G + * 3: GB + * 4: B + * 5: R B + * 6: RGB + * 7: rainbow + * 8: voltage + */ +const PROGMEM uint8_t rgb_led_colors[] = { + 0b00000000, // 0: black + 0b00000001, // 1: red + 0b00000101, // 2: yellow + 0b00000100, // 3: green + 0b00010100, // 4: cyan + 0b00010000, // 5: blue + 0b00010001, // 6: purple + 0b00010101, // 7: white +}; +// intentionally 1 higher than total modes, to make "voltage" easier to reach +// (at Hank's request) +#define RGB_LED_NUM_COLORS 11 +#define RGB_LED_NUM_PATTERNS 4 +#ifndef RGB_LED_OFF_DEFAULT +#define RGB_LED_OFF_DEFAULT 0x19 // low, voltage +//#define RGB_LED_OFF_DEFAULT 0x18 // low, rainbow +#endif +#ifndef RGB_LED_LOCKOUT_DEFAULT +#define RGB_LED_LOCKOUT_DEFAULT 0x39 // blinking, voltage +//#define RGB_LED_LOCKOUT_DEFAULT 0x37 // blinking, disco +#endif +#ifndef RGB_RAINBOW_SPEED +#define RGB_RAINBOW_SPEED 0x0f // change color every 16 frames +#endif +#endif + +//#define USE_OLD_BLINKING_INDICATOR +//#define USE_FANCIER_BLINKING_INDICATOR +#ifdef USE_INDICATOR_LED + // bits 2-3 control lockout mode + // bits 0-1 control "off" mode + // modes are: 0=off, 1=low, 2=high, 3=blinking (if TICK_DURING_STANDBY enabled) + #ifndef INDICATOR_LED_DEFAULT_MODE + #ifdef USE_INDICATOR_LED_WHILE_RAMPING + #define INDICATOR_LED_DEFAULT_MODE ((2<<2) + 1) + #else + #define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1) + #endif + #endif +#endif + diff --git a/ui/anduril/battcheck-mode-fsm.h b/ui/anduril/battcheck-mode-fsm.h new file mode 100644 index 0000000..5f8e8ec --- /dev/null +++ b/ui/anduril/battcheck-mode-fsm.h @@ -0,0 +1,18 @@ +// battcheck-mode-fsm.h: FSM config for battery check mode in Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#define USE_BATTCHECK + +#ifdef USE_AUX_RGB_LEDS + // show voltage colors for a few seconds after going to standby + #define USE_POST_OFF_VOLTAGE + #ifndef DEFAULT_POST_OFF_VOLTAGE_SECONDS + #define DEFAULT_POST_OFF_VOLTAGE_SECONDS 4 + #endif + #ifndef POST_OFF_VOLTAGE_BRIGHTNESS + // level at which to switch from low to high aux brightness + #define POST_OFF_VOLTAGE_BRIGHTNESS (RAMP_SIZE/10) + #endif +#endif diff --git a/ui/anduril/battcheck-mode.c b/ui/anduril/battcheck-mode.c new file mode 100644 index 0000000..462540e --- /dev/null +++ b/ui/anduril/battcheck-mode.c @@ -0,0 +1,82 @@ +// battcheck-mode.c: Battery check mode for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "battcheck-mode.h" + +uint8_t battcheck_state(Event event, uint16_t arg) { + ////////// Every action below here is blocked in the simple UI ////////// + #ifdef USE_SIMPLE_UI + if (cfg.simple_ui_active) { + return EVENT_NOT_HANDLED; + } + #endif + + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + + // 2 clicks: next blinky mode + else if (event == EV_2clicks) { + #if defined(USE_THERMAL_REGULATION) + set_state(tempcheck_state, 0); + #elif defined(USE_BEACON_MODE) + set_state(beacon_state, 0); + #elif defined(USE_SOS_MODE) && defined(USE_SOS_MODE_IN_BLINKY_GROUP) + set_state(sos_state, 0); + #endif + return EVENT_HANDLED; + } + + #ifdef DEFAULT_BLINK_CHANNEL + // 3 clicks: next channel mode (specific to number blinky modes) + else if (event == EV_3clicks) { + cfg.blink_channel = (cfg.blink_channel + 1) % NUM_CHANNEL_MODES; + save_config(); + return EVENT_HANDLED; + } + #endif // ifdef DEFAULT_BLINK_CHANNEL + + #ifdef USE_VOLTAGE_CORRECTION + // 7H: voltage config mode + else if (event == EV_click7_hold) { + push_state(voltage_config_state, 0); + return EVENT_HANDLED; + } + #endif + + return EVENT_NOT_HANDLED; +} + +#ifdef USE_VOLTAGE_CORRECTION +// the user can adjust the battery measurements... on a scale of 1 to 13 +// 1 = subtract 0.30V +// 2 = subtract 0.25V +// ... +// 7 = no effect (add 0V) +// 8 = add 0.05V +// ... +// 13 = add 0.30V +void voltage_config_save(uint8_t step, uint8_t value) { + #ifdef USE_POST_OFF_VOLTAGE + if (2 == step) cfg.post_off_voltage = value; + else + #endif + if (value) cfg.voltage_correction = value; +} + +uint8_t voltage_config_state(Event event, uint16_t arg) { + #ifdef USE_POST_OFF_VOLTAGE + #define VOLTAGE_CONFIG_STEPS 2 + #else + #define VOLTAGE_CONFIG_STEPS 1 + #endif + return config_state_base(event, arg, + VOLTAGE_CONFIG_STEPS, + voltage_config_save); +} +#endif // #ifdef USE_VOLTAGE_CORRECTION + diff --git a/ui/anduril/battcheck-mode.h b/ui/anduril/battcheck-mode.h new file mode 100644 index 0000000..b505b68 --- /dev/null +++ b/ui/anduril/battcheck-mode.h @@ -0,0 +1,12 @@ +// battcheck-mode.h: Battery check mode for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +uint8_t battcheck_state(Event event, uint16_t arg); + +#ifdef USE_VOLTAGE_CORRECTION +void voltage_config_save(uint8_t step, uint8_t value); +uint8_t voltage_config_state(Event event, uint16_t arg); +#endif + diff --git a/ui/anduril/beacon-mode.c b/ui/anduril/beacon-mode.c new file mode 100644 index 0000000..5aff508 --- /dev/null +++ b/ui/anduril/beacon-mode.c @@ -0,0 +1,53 @@ +// beacon-mode.c: Beacon mode for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "beacon-mode.h" + +inline void beacon_mode_iter() { + // one iteration of main loop() + if (! button_last_state) { + set_level(memorized_level); + nice_delay_ms(100); + set_level(0); + nice_delay_ms(((cfg.beacon_seconds) * 1000) - 100); + } +} + +uint8_t beacon_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // TODO: use sleep ticks to measure time between pulses, + // to save power + + // 2 clicks: next blinky mode + else if (event == EV_2clicks) { + #if defined(USE_SOS_MODE) && defined(USE_SOS_MODE_IN_BLINKY_GROUP) + set_state(sos_state, 0); + #elif defined(USE_BATTCHECK) + set_state(battcheck_state, 0); + #elif defined(USE_THERMAL_REGULATION) + set_state(tempcheck_state, 0); + #endif + return EVENT_HANDLED; + } + // hold: configure beacon timing + else if (event == EV_click1_hold) { + if (0 == (arg % TICKS_PER_SECOND)) { + blink_once(); + } + return EVENT_HANDLED; + } + // release hold: save new timing + else if (event == EV_click1_hold_release) { + cfg.beacon_seconds = 1 + (arg / TICKS_PER_SECOND); + save_config(); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + diff --git a/ui/anduril/beacon-mode.h b/ui/anduril/beacon-mode.h new file mode 100644 index 0000000..df047ad --- /dev/null +++ b/ui/anduril/beacon-mode.h @@ -0,0 +1,9 @@ +// beacon-mode.h: Beacon mode for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +// beacon mode +uint8_t beacon_state(Event event, uint16_t arg); +inline void beacon_mode_iter(); + diff --git a/ui/anduril/candle-mode.c b/ui/anduril/candle-mode.c new file mode 100644 index 0000000..ab47c34 --- /dev/null +++ b/ui/anduril/candle-mode.c @@ -0,0 +1,136 @@ +// candle-mode.c: Candle mode for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#include "candle-mode.h" + +#ifdef USE_SUNSET_TIMER +#include "sunset-timer.h" +#endif + +uint8_t candle_mode_state(Event event, uint16_t arg) { + static int8_t ramp_direction = 1; + #define MAX_CANDLE_LEVEL (MAX_LEVEL-CANDLE_AMPLITUDE-15) + static uint8_t candle_wave1 = 0; + static uint8_t candle_wave2 = 0; + static uint8_t candle_wave3 = 0; + static uint8_t candle_wave2_speed = 0; + // these should add up to 100 + #define CANDLE_WAVE1_MAXDEPTH 30 + #define CANDLE_WAVE2_MAXDEPTH 45 + #define CANDLE_WAVE3_MAXDEPTH 25 + static const uint8_t candle_wave1_depth = CANDLE_WAVE1_MAXDEPTH * CANDLE_AMPLITUDE / 100; + static uint8_t candle_wave2_depth = CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100; + static uint8_t candle_wave3_depth = CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100; + static uint8_t candle_mode_brightness = 24; + + #ifdef USE_SUNSET_TIMER + // let the candle "burn out" and shut itself off + // if the user told it to + // cache this in case it changes when the timer is called + uint8_t sunset_active = sunset_timer; + // clock tick + sunset_timer_state(event, arg); + // if the timer just expired, shut off + if (sunset_active && (! sunset_timer)) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + #endif // ifdef USE_SUNSET_TIMER + + + if (event == EV_enter_state) { + ramp_direction = 1; + return EVENT_HANDLED; + } + #ifdef USE_SUNSET_TIMER + // 2 clicks: cancel timer + else if (event == EV_2clicks) { + // parent state just rotated through strobe/flasher modes, + // so cancel timer... in case any time was left over from earlier + sunset_timer = 0; + return EVENT_HANDLED; + } + #endif // ifdef USE_SUNSET_TIMER + // hold: change brightness (brighter) + else if (event == EV_click1_hold) { + // ramp away from extremes + if (! arg) { + if (candle_mode_brightness >= MAX_CANDLE_LEVEL) { ramp_direction = -1; } + else if (candle_mode_brightness <= 1) { ramp_direction = 1; } + } + // change brightness, but not too far + candle_mode_brightness += ramp_direction; + if (candle_mode_brightness < 1) candle_mode_brightness = 1; + else if (candle_mode_brightness > MAX_CANDLE_LEVEL) candle_mode_brightness = MAX_CANDLE_LEVEL; + return EVENT_HANDLED; + } + // reverse ramp direction on hold release + else if (event == EV_click1_hold_release) { + ramp_direction = -ramp_direction; + return EVENT_HANDLED; + } + // click, hold: change brightness (dimmer) + else if (event == EV_click2_hold) { + ramp_direction = 1; + if (candle_mode_brightness > 1) + candle_mode_brightness --; + return EVENT_HANDLED; + } + // clock tick: animate candle brightness + else if (event == EV_tick) { + // un-reverse after 1 second + if (arg == AUTO_REVERSE_TIME) ramp_direction = 1; + + // 3-oscillator synth for a relatively organic pattern + uint8_t add; + add = ((triangle_wave(candle_wave1) * candle_wave1_depth) >> 8) + + ((triangle_wave(candle_wave2) * candle_wave2_depth) >> 8) + + ((triangle_wave(candle_wave3) * candle_wave3_depth) >> 8); + uint16_t brightness = candle_mode_brightness + add; + + // self-timer dims the light during the final minute + #ifdef USE_SUNSET_TIMER + if (1 == sunset_timer) { + brightness = brightness + * ((TICKS_PER_MINUTE>>5) - (sunset_ticks>>5)) + / (TICKS_PER_MINUTE>>5); + } + #endif // ifdef USE_SUNSET_TIMER + + set_level(brightness); + + // wave1: slow random LFO + // TODO: make wave slower and more erratic? + if ((arg & 1) == 0) candle_wave1 += pseudo_rand() & 1; + // wave2: medium-speed erratic LFO + candle_wave2 += candle_wave2_speed; + // wave3: erratic fast wave + candle_wave3 += pseudo_rand() % 37; + // S&H on wave2 frequency to make it more erratic + if ((pseudo_rand() & 0b00111111) == 0) + candle_wave2_speed = pseudo_rand() % 13; + // downward sawtooth on wave2 depth to simulate stabilizing + if ((candle_wave2_depth > 0) && ((pseudo_rand() & 0b00111111) == 0)) + candle_wave2_depth --; + // random sawtooth retrigger + if (pseudo_rand() == 0) { + // random amplitude + //candle_wave2_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2)); + candle_wave2_depth = pseudo_rand() % (CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100); + //candle_wave3_depth = 5; + candle_wave2 = 0; + } + // downward sawtooth on wave3 depth to simulate stabilizing + if ((candle_wave3_depth > 2) && ((pseudo_rand() & 0b00011111) == 0)) + candle_wave3_depth --; + if ((pseudo_rand() & 0b01111111) == 0) + // random amplitude + //candle_wave3_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2)); + candle_wave3_depth = pseudo_rand() % (CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + diff --git a/ui/anduril/candle-mode.h b/ui/anduril/candle-mode.h new file mode 100644 index 0000000..aab237d --- /dev/null +++ b/ui/anduril/candle-mode.h @@ -0,0 +1,13 @@ +// candle-mode.h: Candle mode for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later +#pragma once + +#ifndef CANDLE_AMPLITUDE +#define CANDLE_AMPLITUDE 25 +#endif + +uint8_t candle_mode_state(Event event, uint16_t arg); +// moved to fsm-misc.c because it's also used for tint ramping power correction +//uint8_t triangle_wave(uint8_t phase); + diff --git a/ui/anduril/channel-modes.c b/ui/anduril/channel-modes.c new file mode 100644 index 0000000..b2fc8d1 --- /dev/null +++ b/ui/anduril/channel-modes.c @@ -0,0 +1,237 @@ +// channel-modes.c: Multi-channel functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "channel-modes.h" + +uint8_t channel_mode_state(Event event, uint16_t arg) { + #ifdef USE_CHANNEL_MODE_ARGS + static int8_t tint_ramp_direction = 1; + static uint8_t prev_tint = 0; + // don't activate auto-tint modes unless the user hits the edge + // and keeps pressing for a while + static uint8_t past_edge_counter = 0; + // bugfix: click-click-hold from off to strobes would invoke tint ramping + // in addition to changing state... so ignore any tint-ramp events which + // don't look like they were meant to be here + static uint8_t active = 0; + uint8_t tint = cfg.channel_mode_args[channel_mode]; + #endif + + // it's possible that a light may need 3H but not 3C, + // so try to detect if 3C is needed + #if NUM_CHANNEL_MODES > 1 + // 3 clicks: next channel mode + if (event == EV_3clicks) { + uint8_t next = channel_mode; + // go to next channel mode until we find one which is enabled + // (and don't do any infinite loops if the user disabled them all) + uint8_t count = 0; + do { + count ++; + next = (next + 1) % NUM_CHANNEL_MODES; + } while ((! channel_mode_enabled(next)) && count < NUM_CHANNEL_MODES); + //} while ((! channel_modes_enabled[next]) && count < NUM_CHANNEL_MODES); + + // undo change if infinite loop detected (redundant?) + //if (NUM_CHANNEL_MODES == count) next = channel_mode; + + // if mode hasn't changed, abort + if (channel_mode == next) + return EVENT_NOT_HANDLED; + + set_channel_mode(next); + + // remember after battery changes + cfg.channel_mode = channel_mode; + save_config(); + return EVENT_HANDLED; + } else + #endif // if NUM_CHANNEL_MODES > 1 + + #ifdef USE_CUSTOM_CHANNEL_3H_MODES + // defer to mode-specific function if defined + if (channel_3H_modes[channel_mode]) { + StatePtr tint_func = channel_3H_modes[channel_mode]; + uint8_t err = tint_func(event, arg); + if (EVENT_HANDLED == err) return EVENT_HANDLED; + // else let the default handler run + } + #endif + #ifdef USE_CHANNEL_MODE_ARGS + #ifndef DONT_USE_DEFAULT_CHANNEL_ARG_MODE + // click, click, hold: change the current channel's arg (like tint) + if (event == EV_click3_hold) { + ///// adjust value from 0 to 255 + // reset at beginning of movement + if (! arg) { + active = 1; // first frame means this is for us + past_edge_counter = 0; // doesn't start until user hits the edge + } + // ignore event if we weren't the ones who handled the first frame + if (! active) return EVENT_NOT_HANDLED; + + #ifdef USE_STEPPED_TINT_RAMPING + if ((tint_ramp_direction > 0 && tint < 255) || + (tint_ramp_direction < 0 && tint > 0)) { + // ramp slower in stepped mode + if (cfg.tint_ramp_style && (arg % HOLD_TIMEOUT != 0)) + return EVENT_HANDLED; + + const uint8_t step_size = (cfg.tint_ramp_style < 2) + ? 1 : 254 / (cfg.tint_ramp_style-1); + tint = nearest_tint_value( + tint + ((int16_t)step_size * tint_ramp_direction) + ); + } + #else // smooth tint ramping only + if ((tint_ramp_direction > 0) && (tint < 255)) { tint ++; } + else + if ((tint_ramp_direction < 0) && (tint > 0)) { tint --; } + #endif // ifdef USE_STEPPED_TINT_RAMPING + + // if tint change stalled, let user know we hit the edge + else if (prev_tint == tint) { + if (past_edge_counter == 0) blip(); + past_edge_counter = 1; + } + prev_tint = tint; + cfg.channel_mode_args[channel_mode] = tint; + set_level(actual_level); + return EVENT_HANDLED; + } + + // click, click, hold, release: reverse direction for next ramp + else if (event == EV_click3_hold_release) { + active = 0; // ignore next hold if it wasn't meant for us + // reverse + tint_ramp_direction = -tint_ramp_direction; + if (0 == tint) tint_ramp_direction = 1; + else if (255 == tint) tint_ramp_direction = -1; + // remember tint after battery change + cfg.channel_mode_args[channel_mode] = tint; + save_config(); + // bug?: for some reason, brightness can seemingly change + // from 1/150 to 2/150 without this next line... not sure why + set_level(actual_level); + return EVENT_HANDLED; + } + #endif // ifndef DONT_USE_DEFAULT_CHANNEL_ARG_MODE + #endif // ifdef USE_CHANNEL_MODE_ARGS + + #if defined(USE_SIMPLE_UI) + // remaining mappings aren't "simple", so stop here + if (cfg.simple_ui_active) { + return EVENT_NOT_HANDLED; + } + #endif + + #if NUM_CHANNEL_MODES > 1 + // channel toggle menu on ... 9H? + else if (event == EV_click9_hold) { + push_state(channel_mode_config_state, 0); + return EVENT_HANDLED; + } + #endif + + return EVENT_NOT_HANDLED; +} + + +#if NUM_CHANNEL_MODES > 1 +void channel_mode_config_save(uint8_t step, uint8_t value) { + // 1 menu item per channel mode, to enable or disable that mode + step --; // step is 1-based, channel modes are 0-based + if (value) channel_mode_enable(step); + else channel_mode_disable(step); +} + +uint8_t channel_mode_config_state(Event event, uint16_t arg) { + uint8_t ret; + // make config steps match channel modes + config_color_per_step = true; + // 1 menu item per channel mode, to enable or disable that mode + ret = config_state_base( + event, arg, + NUM_CHANNEL_MODES, + channel_mode_config_save + ); + // no other menu needs this + config_color_per_step = false; + return ret; +} +#endif + + +#if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING) +uint8_t nearest_tint_value(const int16_t target) { + // const symbols for more readable code, will be removed by the compiler + const uint8_t tint_min = 0; + const uint8_t tint_max = 255; + const uint8_t tint_range = tint_max - tint_min; + + // only equal mix of both channels + if (1 == cfg.tint_ramp_style) return (tint_min + tint_max) >> 1; + + if (target < tint_min) return tint_min; + if (target > tint_max) return tint_max; + if (0 == cfg.tint_ramp_style) return target; // smooth ramping + + const uint8_t step_size = tint_range / (cfg.tint_ramp_style-1); + + uint8_t tint_result = tint_min; + for (uint8_t i=0; i>1)) return tint_result; + } + return tint_result; +} +#endif + +#ifdef USE_CIRCULAR_TINT_3H +uint8_t circular_tint_3h(Event event, uint16_t arg) { + static int8_t tint_ramp_direction = 1; + // bugfix: click-click-hold from off to strobes would invoke tint ramping + // in addition to changing state... so ignore any tint-ramp events which + // don't look like they were meant to be here + static uint8_t active = 0; + uint8_t tint = cfg.channel_mode_args[channel_mode]; + + // click, click, hold: change the current channel's arg (like tint) + if (event == EV_click3_hold) { + ///// adjust value from 0 to 255 in a circle + // reset at beginning of movement + if (! arg) { + active = 1; // first frame means this is for us + } + // ignore event if we weren't the ones who handled the first frame + if (! active) return EVENT_NOT_HANDLED; + + // smooth tint ramping only + tint += tint_ramp_direction; + + cfg.channel_mode_args[channel_mode] = tint; + set_level(actual_level); + return EVENT_HANDLED; + } + + // click, click, hold, release: reverse direction for next ramp + else if (event == EV_click3_hold_release) { + active = 0; // ignore next hold if it wasn't meant for us + // reverse + tint_ramp_direction = -tint_ramp_direction; + // remember tint after battery change + save_config(); + // bug?: for some reason, brightness can seemingly change + // from 1/150 to 2/150 without this next line... not sure why + set_level(actual_level); + return EVENT_HANDLED; + } + + return EVENT_NOT_HANDLED; +} +#endif diff --git a/ui/anduril/channel-modes.h b/ui/anduril/channel-modes.h new file mode 100644 index 0000000..b51721d --- /dev/null +++ b/ui/anduril/channel-modes.h @@ -0,0 +1,26 @@ +// channel-modes.h: Multi-channel functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +// not actually a mode, more of a fallback under other modes +uint8_t channel_mode_state(Event event, uint16_t arg); + +#if NUM_CHANNEL_MODES > 1 +uint8_t channel_mode_config_state(Event event, uint16_t arg); +#endif + +#if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING) +// calculate the nearest tint value which would be valid at the moment +uint8_t nearest_tint_value(const int16_t target); +#endif + +#ifdef USE_CUSTOM_CHANNEL_3H_MODES +StatePtr channel_3H_modes[NUM_CHANNEL_MODES]; +#endif + +#ifdef USE_CIRCULAR_TINT_3H +uint8_t circular_tint_3h(Event event, uint16_t arg); +#endif + diff --git a/ui/anduril/config-default.h b/ui/anduril/config-default.h new file mode 100644 index 0000000..899bc4a --- /dev/null +++ b/ui/anduril/config-default.h @@ -0,0 +1,207 @@ +// config-default.h: Default configuration for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +/* + * This file specifies the default settings for Anduril. + * + * These settings can be overridden per build target, in cfg-*.h files... + * ... but most are not. So changing one here will make it change in + * almost every build target. + * + * Some configurable settings are also in other *.h files. + */ + +/********* User-configurable options *********/ +// low voltage protection (also required for battery check mode) +#define USE_LVP + +// overheat protection +#define USE_THERMAL_REGULATION +#if (ATTINY==85) || (ATTINY==1634) +// sloppy temperature sensor needs bigger error margin +#define DEFAULT_THERM_CEIL 45 // try not to get hotter than this (in C) +#else +// more accurate temperature sensor can regulate higher safely +#define DEFAULT_THERM_CEIL 50 // try not to get hotter than this (in C) +#endif +// Comment out to disable automatic calibration on factory reset +// - If so, be sure to set THERM_CAL_OFFSET to the correct calibration offset +// - Calibration can still be overridden in temperature check mode +// Or uncomment to use the default auto-calibrate on factory reset +// +// To determine THERM_CAL_OFFSET, comment out USE_THERM_AUTOCALIBRATE to +// disable auto-calibration, compile and flash, let flashlight rest at a known +// temperature, then enter temp check mode (do NOT enter calibration mode). +// +// THERM_CAL_OFFSET = known_temperature - temp_check_blinks + THERM_CAL_OFFSET +// +// (include THERM_CAL_OFFSET in sum as it might already be a non-zero number) +#define USE_THERM_AUTOCALIBRATE + +// Include a simplified UI for non-enthusiasts? +#define USE_SIMPLE_UI + + +///// Ramp mode options ///// + +// button timing for turning light on/off: +// B_PRESS_T: activate as soon as button is pressed +// B_RELEASE_T: activate when user lets go of button +// B_TIMEOUT_T: activate when we're sure the user won't double-click +// defaults are release on, timeout off +#define B_TIMING_ON B_RELEASE_T +#define B_TIMING_OFF B_TIMEOUT_T + +// default ramp style: 0 = smooth, 1 = stepped +#define RAMP_STYLE 0 + +// smooth ramp speed: 1, 2, 3, 4, ... for 1X speed, 1/2, 1/3rd, 1/4th, ... +#define USE_RAMP_SPEED_CONFIG + +// after ramping, how long until the direction resets to "up"? +#define AUTO_REVERSE_TIME (TICKS_PER_SECOND * 2 / 3) + +// add runtime option for whether hold-from-off should ramp or stay at moon +#define USE_RAMP_AFTER_MOON_CONFIG + +// short blip when crossing from "click" to "hold" from off +// (helps the user hit moon mode exactly, instead of holding too long +// or too short) +#define MOON_TIMING_HINT // only applies if B_TIMING_ON == B_PRESS_T +// short blips while ramping +#define BLINK_AT_RAMP_MIDDLE +//#define BLINK_AT_RAMP_FLOOR +#define BLINK_AT_RAMP_CEIL +//#define BLINK_AT_STEPS // whenever a discrete ramp mode is passed in smooth mode + +// Uncomment for Anduril1 "Ramp 2C" behavior: +// - Ramp 2C goes to turbo (advanced UI) or ceiling (simple UI), like in Anduril1 +// Or comment out to use Anduril2 behavior instead: +// - Ramp 2C goes to ceiling, unless already at ceiling or in simple UI. +// (Advanced UI ceiling 2C goes to turbo) +//#define USE_2C_MAX_TURBO +// Or uncomment to let the user decide which style they want: +#define USE_2C_STYLE_CONFIG +// 0 = no turbo +// 1 = A1 style: Off 2C = ceil, On 2C = turbo +// 2 = A2 style: Off 2C = ceil, On 2C = ceil, Ramped ceil 2C = turbo +// All styles allow momentary turbo in advanced UI +//#define DEFAULT_2C_STYLE 2 // default to Anduril 2 style +//#define DEFAULT_2C_STYLE_SIMPLE 0 // no turbo at all + +// make the ramps configurable by the user +#define USE_RAMP_CONFIG + +// adds a runtime option to switch between automatic memory (default) +// and manual memory (only available if compiled in) +// (manual memory makes 1-click-from-off start at the same level each time) +// (the level can be set explicitly with 10 clicks from on, +// or the user can go back to automatic with 10H) +#define USE_MANUAL_MEMORY +// if enabled, user can use "hybrid memory" +// The light will use automatic or manual memory level, depending on how long +// the light was off. Short off = automatic, long off = manual. +// This also remaps 10C/10H: +// - 10C toggles manual mem on/off at current level. +// - 10H configures the timer value. +#define USE_MANUAL_MEMORY_TIMER + +// enable sunset timer (ramp-down and automatic shutoff) +// timer is available in regular ramp mode and candle mode +#define USE_SUNSET_TIMER + + +///// What to do when power is connected ///// +// factory reset function erases user's runtime configuration in eeprom +#define USE_FACTORY_RESET +//#define USE_SOFT_FACTORY_RESET // only needed on models which can't use hold-button-at-boot + +// dual-switch support (second switch is a tail clicky) +// (currently incompatible with factory reset) +//#define START_AT_MEMORIZED_LEVEL + + +///// extra modes (enable / disable / configure each mode) ///// + +// include a function to blink out the firmware version +#define USE_VERSION_CHECK + +// enable the battery check mode (shows remaining charge) (requires USE_LVP) +#define USE_BATTCHECK_MODE +// battery readout style (pick one) +// TODO: allow VpT and 4-bar simultaneously, +// so one can be in "simple mode" and the other in "advanced mode" +#define BATTCHECK_VpT +//#define BATTCHECK_8bars // FIXME: breaks build +//#define BATTCHECK_4bars // FIXME: breaks build +// allow the user to calibrate the voltage readings? +// (adjust in 0.05V increments from -0.30V to +0.30V) +// (1 = -0.30V, 2 = -0.25V, ... 7 = 0V, ... 13 = +0.30V) +#define USE_VOLTAGE_CORRECTION + +// enable beacon mode +#define USE_BEACON_MODE + +// enable/disable various strobe modes +#define USE_BIKE_FLASHER_MODE +#define USE_PARTY_STROBE_MODE +#define USE_TACTICAL_STROBE_MODE +#define USE_LIGHTNING_MODE +#define USE_CANDLE_MODE + +// boring strobes nobody really likes, but sometimes flashlight companies want +// (these replace the fun strobe group, +// so don't enable them at the same time as any of the above strobes) +//#define USE_POLICE_STROBE_MODE +#define USE_SOS_MODE +//#define USE_SOS_MODE_IN_FF_GROUP // put SOS in the "boring strobes" mode +#define USE_SOS_MODE_IN_BLINKY_GROUP // put SOS in the blinkies mode group + +// enable a mode for locking the light for safe carry +#define USE_LOCKOUT_MODE +// should lockout mode function as a momentary moon mode? +#define USE_MOON_DURING_LOCKOUT_MODE +// add an optional setting to lock the light after being off for a while +#define USE_AUTOLOCK + +// enable momentary mode +#define USE_MOMENTARY_MODE + +// enable tactical mode +#define USE_TACTICAL_MODE + + +// enable a shortcut for +10 in number entry mode +// (click for +1, hold for +10) +#define USE_NUMBER_ENTRY_PLUS10 + +// cut clock speed at very low modes for better efficiency +// (defined here so config files can override it) +#define USE_DYNAMIC_UNDERCLOCKING + +// if the aux LEDs oscillate between "full battery" and "empty battery" +// while in "voltage" mode, enable this to reduce the amplitude of +// those oscillations +#if (ATTINY==1616) || (ATTINY==1634) +#define USE_LOWPASS_WHILE_ASLEEP +#endif + +// if there's tint ramping, allow user to set it smooth or stepped +#define USE_STEPPED_TINT_RAMPING +#define DEFAULT_TINT_RAMP_STYLE 0 // smooth + +// Use "smooth steps" to soften on/off and step changes +// on MCUs with enough room for extra stuff like this +#if (ATTINY==1616) || (ATTINY==1634) +#define USE_SMOOTH_STEPS +#endif +// 0 = none, 1 = smooth, 2+ = undefined +#define DEFAULT_SMOOTH_STEPS_STYLE 1 + +// by default, allow user to set the channel for each strobe-group mode +// (but allow disabling this feature per build) +#define USE_CHANNEL_PER_STROBE + diff --git a/ui/anduril/config-mode.c b/ui/anduril/config-mode.c new file mode 100644 index 0000000..71b0d69 --- /dev/null +++ b/ui/anduril/config-mode.c @@ -0,0 +1,196 @@ +// config-mode.c: Config mode base functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "config-mode.h" + +// general helper function for config modes +uint8_t number_entry_state(Event event, uint16_t arg); +// return value from number_entry_state() +volatile uint8_t number_entry_value; + + +#if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1) +// TODO: promote this to fsm-channels.c ? +void set_chan_if(bool cond, uint8_t chan) { + if ((cond) && (chan != channel_mode)) + set_channel_mode(chan); +} +#endif + +// allow the user to set a new value for a config option +// can be called two ways: +// - with a "click" action: Configures first menu item only. +// - with a "hold" action: Sets user select a menu item and then +// choose a new value for it. User should hold button until light +// blinks N times, to choose menu item N. Then let go, and light +// should give a buzzing prompt to enter a number. Click N times +// at the prompt to set the new value to N. +// after completing this process, config state calls the savefunc callback +// and then returns to caller/parent state +uint8_t config_state_base( + Event event, + uint16_t arg, + uint8_t num_config_steps, + void (*savefunc)(uint8_t step, uint8_t value)) { + + static uint8_t config_step; + #ifdef USE_CONFIG_COLORS + static uint8_t orig_channel; + #endif + if (event == EV_enter_state) { + #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1) + orig_channel = channel_mode; + #endif + config_step = 0; + set_level(0); + // if button isn't held, configure first menu item + if (! button_last_state) { + config_step ++; + push_state(number_entry_state, 0); + } + } + + // if initial "hold" event still active + // blink while holding to indicate option number + #define B_CLICK_FLAGS (B_CLICK|B_HOLD|B_PRESS|B_RELEASE|B_TIMEOUT) + #define B_ANY_HOLD (B_CLICK|B_HOLD|B_PRESS) + #define B_ANY_HOLD_RELEASE (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT) + else if ((event & B_CLICK_FLAGS) == B_ANY_HOLD) { + if (config_step <= num_config_steps) { + #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1) + uint8_t chan = config_step - 1; + if (chan < NUM_CHANNEL_MODES) + set_chan_if(config_color_per_step, chan); + #endif + if ((TICKS_PER_SECOND/10) == (arg % (TICKS_PER_SECOND*3/2))) { + config_step ++; + // blink when config step advances + if (config_step <= num_config_steps) { + #ifdef CONFIG_BLINK_CHANNEL + set_chan_if(!config_color_per_step, CONFIG_BLINK_CHANNEL); + #endif + set_level(RAMP_SIZE * 3 / 8); + } + } + else { + // stay on at a low level to indicate menu is active + #ifdef CONFIG_WAITING_CHANNEL + set_chan_if(!config_color_per_step, CONFIG_WAITING_CHANNEL); + #endif + set_level(RAMP_SIZE * 1 / 8); + } + } else { + // turn off when end of menu is reached + set_level(0); + } + } + + // button release: activate number entry for one menu item + else if ((event & B_CLICK_FLAGS) == B_ANY_HOLD_RELEASE) { + // ask the user for a number, if they selected a menu item + if (config_step && config_step <= num_config_steps) { + #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1) + // put the colors back how they were + set_channel_mode(orig_channel); + #endif + push_state(number_entry_state, 0); + } + // exit after falling out of end of menu + else { + pop_state(); + } + } + + // an option was set (return from number_entry_state) + else if (event == EV_reenter_state) { + // process value with parent's callback + savefunc(config_step, number_entry_value); + // make changes persist in eeprom + save_config(); + pop_state(); + } + + #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1) + else if (event == EV_leave_state) { + // put the colors back how they were + set_channel_mode(orig_channel); + } + #endif + + // eat all other events; don't pass any through to parent + return EVENT_HANDLED; +} + +uint8_t number_entry_state(Event event, uint16_t arg) { + static uint8_t entry_step; + + if (event == EV_enter_state) { + number_entry_value = 0; + entry_step = 0; + set_level(0); // initial pause should be dark + } + + // advance through the process: + // 0: wait a moment + // 1: "buzz" while counting clicks + // 2: save and exit + else if (event == EV_tick) { + // wait a moment + if (entry_step == 0) { + if (arg > TICKS_PER_SECOND/2) { + entry_step ++; + empty_event_sequence(); // reset tick counter to 0 + } + } + // buzz while waiting for a number to be entered + else if (entry_step == 1) { + // time out and exit after 3 seconds + if (arg > TICKS_PER_SECOND*3) { + entry_step ++; + set_level(0); + } + // buzz for N seconds after last event + // (flicker every other frame, + // except first frame (so we can see flashes after each click)) + else if (arg) { + #ifdef CONFIG_WAITING_CHANNEL + set_chan_if(1, CONFIG_WAITING_CHANNEL); + #endif + set_level( (RAMP_SIZE/8) + + ((arg&2)<<2) ); + } + } + // all done, save result and return to parent state + else { + pop_state(); + } + return EVENT_HANDLED; + } + + // count clicks: click = +1, hold = +10 + else if ((event == EV_click1_release) + #ifdef USE_NUMBER_ENTRY_PLUS10 + || (event == EV_click1_hold_release) + #endif + ) { + entry_step = 1; // in case user clicked during initial delay + #ifdef USE_NUMBER_ENTRY_PLUS10 + if (event == EV_click1_hold_release) number_entry_value += 10; + else + #endif + number_entry_value ++; // update the result + empty_event_sequence(); // reset FSM's click count + #ifdef CONFIG_BLINK_CHANNEL + set_channel_mode(CONFIG_BLINK_CHANNEL); + #endif + set_level(RAMP_SIZE/2); // flash briefly + return EVENT_HANDLED; + } + + // eat all other events; don't pass any through to parent + return EVENT_HANDLED; +} + diff --git a/ui/anduril/config-mode.h b/ui/anduril/config-mode.h new file mode 100644 index 0000000..d4a7652 --- /dev/null +++ b/ui/anduril/config-mode.h @@ -0,0 +1,24 @@ +// config-mode.h: Config mode base functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +// menus can use 2 colors +#if defined (CONFIG_WAITING_CHANNEL) || defined(CONFIG_BLINK_CHANNEL) +#define USE_CONFIG_COLORS +#endif + +#if NUM_CHANNEL_MODES > 1 +// when true, changes the channel mode for each config step +bool config_color_per_step = false; +#endif + +// config menu +uint8_t config_state_base( + Event event, + uint16_t arg, + uint8_t num_config_steps, + void (*savefunc)(uint8_t step, uint8_t value) + ); + diff --git a/ui/anduril/factory-reset-fsm.h b/ui/anduril/factory-reset-fsm.h new file mode 100644 index 0000000..3cb0875 --- /dev/null +++ b/ui/anduril/factory-reset-fsm.h @@ -0,0 +1,10 @@ +// factory-reset-fsm.h: FSM config options to enable factory reset in Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#ifdef USE_SOFT_FACTORY_RESET +#define USE_REBOOT +#endif + diff --git a/ui/anduril/factory-reset.c b/ui/anduril/factory-reset.c new file mode 100644 index 0000000..f9fb472 --- /dev/null +++ b/ui/anduril/factory-reset.c @@ -0,0 +1,73 @@ +// factory-reset.c: Factory reset functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "factory-reset.h" + +// allows setting channel mode per animation stage, +// so it can ramp up in red then explode in white (as one example) + +void factory_reset() { + // display a warning for a few seconds before doing the actual reset, + // so the user has time to abort if they want + #define SPLODEY_TIME 2500 + #define SPLODEY_STEPS 64 + #define SPLODEY_TIME_PER_STEP (SPLODEY_TIME/SPLODEY_STEPS) + uint8_t bright; + uint8_t reset = 1; + // wind up to an explosion + #ifdef FACTORY_RESET_WARN_CHANNEL + set_channel_mode(FACTORY_RESET_WARN_CHANNEL); + #endif + for (bright=0; bright>1); + nice_delay_ms(SPLODEY_TIME_PER_STEP/2); + if (! button_is_pressed()) { + reset = 0; + break; + } + } + // explode, if button pressed long enough + if (reset) { + #if defined(FACTORY_RESET_WARN_CHANNEL) && defined(DEFAULT_CHANNEL_MODE) + // return to default channel before saving + set_channel_mode(DEFAULT_CHANNEL_MODE); + #endif + + // auto-calibrate temperature + // AVR 1-Series has factory calibrated thermal sensor, always remove the offset on reset + #if defined(USE_THERMAL_REGULATION) && defined(AVRXMEGA3) + // this will cancel out the offset + thermal_config_save(1, temperature - cfg.therm_cal_offset); + #elif defined(USE_THERMAL_REGULATION) && defined(USE_THERM_AUTOCALIBRATE) + // assume current temperature is 21 C + thermal_config_save(1, 21); + #endif + + // save all settings to eeprom + // (assuming they're all at default because we haven't loaded them yet) + save_config(); + + // explosion animation + #ifdef FACTORY_RESET_SUCCESS_CHANNEL + set_channel_mode(FACTORY_RESET_SUCCESS_CHANNEL); + #endif + bright = MAX_LEVEL; + for (; bright > 0; bright--) { + set_level(bright); + nice_delay_ms(SPLODEY_TIME_PER_STEP/8); + } + } + // explosion cancelled, fade away + else { + for (; bright > 0; bright--) { + set_level(bright); + nice_delay_ms(SPLODEY_TIME_PER_STEP/3); + } + } +} + diff --git a/ui/anduril/factory-reset.h b/ui/anduril/factory-reset.h new file mode 100644 index 0000000..63c25cd --- /dev/null +++ b/ui/anduril/factory-reset.h @@ -0,0 +1,8 @@ +// factory-reset.h: Factory reset functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +void factory_reset(); + diff --git a/ui/anduril/ff-strobe-modes.c b/ui/anduril/ff-strobe-modes.c new file mode 100644 index 0000000..b7a7303 --- /dev/null +++ b/ui/anduril/ff-strobe-modes.c @@ -0,0 +1,62 @@ +// ff-strobe-modes.c: Fireflies Flashlights strobe modes for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "ff-strobe-modes.h" + +uint8_t boring_strobe_state(Event event, uint16_t arg) { + // police strobe and SOS, meh + // 'st' reduces ROM size slightly + uint8_t st = boring_strobe_type; + + if (event == EV_enter_state) { + return EVENT_HANDLED; + } + // 1 click: off + else if (event == EV_1click) { + // reset to police strobe for next time + boring_strobe_type = 0; + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: rotate through strobe/flasher modes + else if (event == EV_2clicks) { + boring_strobe_type = (st + 1) % NUM_BORING_STROBES; + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + +inline void boring_strobe_state_iter() { + switch(boring_strobe_type) { + #ifdef USE_POLICE_STROBE_MODE + case 0: // police strobe + police_strobe_iter(); + break; + #endif + + #ifdef USE_SOS_MODE_IN_FF_GROUP + default: // SOS + sos_mode_iter(); + break; + #endif + } +} + +#ifdef USE_POLICE_STROBE_MODE +inline void police_strobe_iter() { + // one iteration of main loop() + // flash at 16 Hz then 8 Hz, 8 times each + for (uint8_t del=41; del<100; del+=41) { + for (uint8_t f=0; f<8; f++) { + set_level(STROBE_BRIGHTNESS); + nice_delay_ms(del >> 1); + set_level(0); + nice_delay_ms(del); + } + } +} +#endif + diff --git a/ui/anduril/ff-strobe-modes.h b/ui/anduril/ff-strobe-modes.h new file mode 100644 index 0000000..d7adfec --- /dev/null +++ b/ui/anduril/ff-strobe-modes.h @@ -0,0 +1,15 @@ +// ff-strobe-modes.h: Fireflies Flashlights strobe modes for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +uint8_t boring_strobe_state(Event event, uint16_t arg); +inline void boring_strobe_state_iter(); +uint8_t boring_strobe_type = 0; +void sos_blink(uint8_t num, uint8_t dah); +#ifdef USE_POLICE_STROBE_MODE +inline void police_strobe_iter(); +#endif +#define NUM_BORING_STROBES 2 + diff --git a/ui/anduril/load-save-config-fsm.h b/ui/anduril/load-save-config-fsm.h new file mode 100644 index 0000000..d189d3a --- /dev/null +++ b/ui/anduril/load-save-config-fsm.h @@ -0,0 +1,139 @@ +// load-save-config-fsm.h: FSM config for eeprom configuration in Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#define USE_EEPROM +// load into a custom RAM location instead of FSM's default byte array +#define EEPROM_OVERRIDE + +#ifdef USE_SIMPLE_UI +#define NUM_RAMPS 3 +#else +#define NUM_RAMPS 2 +#endif + +// let FSM know this config struct exists +#define USE_CFG + +typedef struct Config { + + ///// ramp vars + uint8_t ramp_style; + #ifdef USE_2C_STYLE_CONFIG + uint8_t ramp_2c_style; + #endif + #ifdef USE_RAMP_CONFIG + uint8_t ramp_floors[NUM_RAMPS]; + uint8_t ramp_ceils [NUM_RAMPS]; + uint8_t ramp_stepss[NUM_RAMPS]; + #endif + #ifdef USE_SIMPLE_UI + uint8_t simple_ui_active; + #ifdef USE_2C_STYLE_CONFIG + uint8_t ramp_2c_style_simple; + #endif + #endif + #ifdef USE_RAMP_AFTER_MOON_CONFIG + uint8_t dont_ramp_after_moon; + #endif + #ifdef USE_MANUAL_MEMORY + uint8_t manual_memory; + #ifdef USE_MANUAL_MEMORY_TIMER + uint8_t manual_memory_timer; + #endif + #endif + + ///// channel modes / color modes + #if NUM_CHANNEL_MODES > 1 + uint8_t channel_mode; + uint16_t channel_modes_enabled; + #ifdef USE_MANUAL_MEMORY + uint8_t manual_memory_channel_mode; + #endif + #ifdef DEFAULT_BLINK_CHANNEL + uint8_t blink_channel; + #endif + #endif + #ifdef USE_CHANNEL_MODE_ARGS + // this is an array, needs a few bytes + uint8_t channel_mode_args[NUM_CHANNEL_MODES]; + #ifdef USE_MANUAL_MEMORY + uint8_t manual_memory_channel_args[NUM_CHANNEL_MODES]; + #endif + #ifdef USE_STEPPED_TINT_RAMPING + uint8_t tint_ramp_style; + #endif + #endif + + ///// Smooth animation between steps, and for on/off + #ifdef USE_SMOOTH_STEPS + uint8_t smooth_steps_style; + #endif + + ///// strobe / blinky mode settings + #ifdef USE_STROBE_STATE + uint8_t strobe_type; + #if (NUM_CHANNEL_MODES > 1) && defined(USE_CHANNEL_PER_STROBE) + uint8_t strobe_channels[NUM_STROBES]; + #endif + #endif + #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) + uint8_t strobe_delays[2]; + #endif + #ifdef USE_BIKE_FLASHER_MODE + uint8_t bike_flasher_brightness; + #endif + #ifdef USE_BEACON_MODE + uint8_t beacon_seconds; + #endif + + ///// voltage and temperature + #ifdef USE_VOLTAGE_CORRECTION + uint8_t voltage_correction; + #endif + #ifdef USE_THERMAL_REGULATION + uint8_t therm_ceil; + int8_t therm_cal_offset; + #endif + + ///// aux LEDs + #ifdef USE_INDICATOR_LED + uint8_t indicator_led_mode; + #endif + #ifdef USE_AUX_RGB_LEDS + uint8_t rgb_led_off_mode; + uint8_t rgb_led_lockout_mode; + #ifdef USE_POST_OFF_VOLTAGE + uint8_t post_off_voltage; + #endif + #endif + + ///// misc other mode settings + #ifdef USE_AUTOLOCK + uint8_t autolock_time; + #endif + #ifdef USE_TACTICAL_MODE + uint8_t tactical_levels[3]; + #endif + + ///// hardware config / globals menu + #ifdef USE_JUMP_START + uint8_t jump_start_level; + #endif + +} Config; + +// auto-detect how many eeprom bytes +#define EEPROM_BYTES sizeof(Config) + +// declare this so FSM can see it, +// but define its values in a file which loads later +Config cfg; + +#ifdef START_AT_MEMORIZED_LEVEL +#define USE_EEPROM_WL +#define EEPROM_WL_BYTES 1 +#endif + diff --git a/ui/anduril/load-save-config.c b/ui/anduril/load-save-config.c new file mode 100644 index 0000000..aa772e1 --- /dev/null +++ b/ui/anduril/load-save-config.c @@ -0,0 +1,33 @@ +// load-save-config.c: Load/save/eeprom functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "load-save-config-fsm.h" +#include "load-save-config.h" + +void load_config() { + eeprom = (uint8_t *)&cfg; + + if (! load_eeprom()) return; + + #ifdef START_AT_MEMORIZED_LEVEL + if (load_eeprom_wl()) { + memorized_level = eeprom_wl[0]; + } + #endif +} + +void save_config() { + eeprom = (uint8_t *)&cfg; + save_eeprom(); +} + +#ifdef START_AT_MEMORIZED_LEVEL +void save_config_wl() { + eeprom_wl[0] = memorized_level; + save_eeprom_wl(); +} +#endif + diff --git a/ui/anduril/load-save-config.h b/ui/anduril/load-save-config.h new file mode 100644 index 0000000..514fcbb --- /dev/null +++ b/ui/anduril/load-save-config.h @@ -0,0 +1,173 @@ +// load-save-config.h: Load/save/eeprom functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +// remember stuff even after battery was changed +void load_config(); +void save_config(); +#ifdef START_AT_MEMORIZED_LEVEL +void save_config_wl(); +#endif + +// a struct to hold config values +Config cfg = { + + ///// ramp vars + + // smooth vs discrete ramping + .ramp_style = RAMP_STYLE, // 0 = smooth, 1 = discrete + #ifdef USE_2C_STYLE_CONFIG + // 1 = A1 style, 2 = A2 style + .ramp_2c_style = DEFAULT_2C_STYLE, + #endif + + // settings for each ramp (smooth, stepped, simple UI) + #ifdef USE_RAMP_CONFIG + .ramp_floors = { + RAMP_SMOOTH_FLOOR, + RAMP_DISCRETE_FLOOR, + #ifdef USE_SIMPLE_UI + SIMPLE_UI_FLOOR, + #endif + }, + .ramp_ceils = { + RAMP_SMOOTH_CEIL, + RAMP_DISCRETE_CEIL, + #ifdef USE_SIMPLE_UI + SIMPLE_UI_CEIL, + #endif + }, + .ramp_stepss = { + DEFAULT_RAMP_SPEED, + RAMP_DISCRETE_STEPS, + #ifdef USE_SIMPLE_UI + SIMPLE_UI_STEPS, + #endif + }, + #endif + + #ifdef USE_SIMPLE_UI + // whether to enable the simplified interface or not + .simple_ui_active = SIMPLE_UI_ACTIVE, + #ifdef USE_2C_STYLE_CONFIG + // 0 = no turbo, 1 = A1 style, 2 = A2 style + .ramp_2c_style_simple = DEFAULT_2C_STYLE_SIMPLE, + #endif + #endif + + #ifdef USE_RAMP_AFTER_MOON_CONFIG + .dont_ramp_after_moon = DEFAULT_DONT_RAMP_AFTER_MOON, + #endif + + #ifdef USE_MANUAL_MEMORY + .manual_memory = DEFAULT_MANUAL_MEMORY, + #ifdef USE_MANUAL_MEMORY_TIMER + .manual_memory_timer = DEFAULT_MANUAL_MEMORY_TIMER, + #endif + #endif + + ///// channel modes / color modes + + #if NUM_CHANNEL_MODES > 1 + // current multi-channel mode + .channel_mode = DEFAULT_CHANNEL_MODE, + // user can take unwanted modes out of the rotation (bitmask) + .channel_modes_enabled = CHANNEL_MODES_ENABLED, + #ifdef USE_MANUAL_MEMORY + // reset w/ manual memory + .manual_memory_channel_mode = DEFAULT_CHANNEL_MODE, + #endif + #ifdef DEFAULT_BLINK_CHANNEL + // blink numbers in a specific channel (user configurable) + .blink_channel = DEFAULT_BLINK_CHANNEL, + #endif + #endif + #ifdef USE_CHANNEL_MODE_ARGS + // one byte of extra data per channel mode, like for tint value + .channel_mode_args = { CHANNEL_MODE_ARGS }, + #ifdef USE_MANUAL_MEMORY + // remember and reset 1 extra parameter per channel mode (like tint) + .manual_memory_channel_args = { CHANNEL_MODE_ARGS }, + #endif + #ifdef USE_STEPPED_TINT_RAMPING + .tint_ramp_style = DEFAULT_TINT_RAMP_STYLE, + #endif + #endif + + ///// Smooth animation between steps, and for on/off + #ifdef USE_SMOOTH_STEPS + .smooth_steps_style = DEFAULT_SMOOTH_STEPS_STYLE, + #endif + + ///// strobe / blinky mode settings + + #ifdef USE_STROBE_STATE + .strobe_type = DEFAULT_STROBE, + #if (NUM_CHANNEL_MODES > 1) && defined(USE_CHANNEL_PER_STROBE) + // channel mode saved per strobe-group mode + #ifdef DEFAULT_STROBE_CHANNELS + .strobe_channels = { DEFAULT_STROBE_CHANNELS }, + #endif + #endif + #endif + #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) + // party / tactical strobe timing + // party strobe 24 Hz, tactical strobe 10 Hz + .strobe_delays = { 41, 67 }, + #endif + #ifdef USE_BIKE_FLASHER_MODE + .bike_flasher_brightness = MAX_1x7135, + #endif + #ifdef USE_BEACON_MODE + // beacon timing + .beacon_seconds = 2, + #endif + + ///// voltage and temperature + + #ifdef USE_VOLTAGE_CORRECTION + // same 0.05V units as fudge factor, + // but 7 is neutral, and the expected range is from 1 to 13 + .voltage_correction = 7, + #endif + #ifdef USE_THERMAL_REGULATION + .therm_ceil = DEFAULT_THERM_CEIL, + .therm_cal_offset = 0, + #endif + + ///// aux LEDs + + #ifdef USE_INDICATOR_LED + // bits 2-3 control lockout mode + // bits 0-1 control "off" mode + // modes are: 0=off, 1=low, 2=high, 3=blinking (if TICK_DURING_STANDBY enabled) + .indicator_led_mode = INDICATOR_LED_DEFAULT_MODE, + #endif + #ifdef USE_AUX_RGB_LEDS + .rgb_led_off_mode = RGB_LED_OFF_DEFAULT, + .rgb_led_lockout_mode = RGB_LED_LOCKOUT_DEFAULT, + #ifdef USE_POST_OFF_VOLTAGE + // display voltage readout for a while after turning off? + .post_off_voltage = DEFAULT_POST_OFF_VOLTAGE_SECONDS, + #endif + #endif + + ///// misc other mode settings + + #ifdef USE_AUTOLOCK + .autolock_time = DEFAULT_AUTOLOCK_TIME, + #endif + #ifdef USE_TACTICAL_MODE + .tactical_levels = { TACTICAL_LEVELS }, + #endif + + ///// hardware config / globals menu + + #ifdef USE_JUMP_START + .jump_start_level = DEFAULT_JUMP_START_LEVEL, + #endif + +}; + diff --git a/ui/anduril/lockout-mode-fsm.h b/ui/anduril/lockout-mode-fsm.h new file mode 100644 index 0000000..ede251c --- /dev/null +++ b/ui/anduril/lockout-mode-fsm.h @@ -0,0 +1,11 @@ +// lockout-mode-fsm.h: FSM config for lockout mode in Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +// autolock function requires the ability to measure time while "off" +#ifdef USE_AUTOLOCK +#define TICK_DURING_STANDBY +#endif + diff --git a/ui/anduril/lockout-mode.c b/ui/anduril/lockout-mode.c new file mode 100644 index 0000000..422d081 --- /dev/null +++ b/ui/anduril/lockout-mode.c @@ -0,0 +1,219 @@ +// lockout-mode.c: Lockout mode for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "lockout-mode.h" + +uint8_t lockout_state(Event event, uint16_t arg) { + #ifdef USE_MOON_DURING_LOCKOUT_MODE + // momentary(ish) moon mode during lockout + // button is being held + #ifdef USE_AUX_RGB_LEDS + // don't turn on during RGB aux LED configuration + if (event == EV_click7_hold) { set_level(0); } else + #endif + if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { + // hold: lowest floor + // click, hold: highest floor (or manual mem level) + uint8_t lvl = cfg.ramp_floors[0]; + if (1 == (event & 0x0f)) { // first click + if (cfg.ramp_floors[1] < lvl) lvl = cfg.ramp_floors[1]; + } else { // 2nd click or later + if (cfg.ramp_floors[1] > lvl) lvl = cfg.ramp_floors[1]; + #ifdef USE_MANUAL_MEMORY + if (cfg.manual_memory) lvl = cfg.manual_memory; + #endif + } + set_level(lvl); + } + // button was released + else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { + set_level(0); + } + #endif // ifdef USE_MOON_DURING_LOCKOUT_MODE + + // regular event handling + // conserve power while locked out + // (allow staying awake long enough to exit, but otherwise + // be persistent about going back to sleep every few seconds + // even if the user keeps pressing the button) + if (event == EV_enter_state) { + ticks_since_on = 0; + #ifdef USE_INDICATOR_LED + // redundant, sleep tick does the same thing + // indicator_led_update(cfg.indicator_led_mode >> 2, 0); + #elif defined(USE_AUX_RGB_LEDS) + rgb_led_update(cfg.rgb_led_lockout_mode, 0); + #endif + } + + else if (event == EV_tick) { + if (arg > HOLD_TIMEOUT) { + go_to_standby = 1; + #ifdef USE_INDICATOR_LED + // redundant, sleep tick does the same thing + //indicator_led_update(cfg.indicator_led_mode >> 2, arg); + #elif defined(USE_AUX_RGB_LEDS) + rgb_led_update(cfg.rgb_led_lockout_mode, arg); + #endif + } + return EVENT_HANDLED; + } + + #if defined(TICK_DURING_STANDBY) && (defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS)) + else if (event == EV_sleep_tick) { + if (ticks_since_on < 255) ticks_since_on ++; + #ifdef USE_MANUAL_MEMORY_TIMER + // reset to manual memory level when timer expires + if (cfg.manual_memory && + (arg >= (cfg.manual_memory_timer * SLEEP_TICKS_PER_MINUTE))) { + manual_memory_restore(); + } + #endif + #if defined(USE_INDICATOR_LED) + indicator_led_update(cfg.indicator_led_mode >> 2, arg); + #elif defined(USE_AUX_RGB_LEDS) + rgb_led_update(cfg.rgb_led_lockout_mode, arg); + #endif + return EVENT_HANDLED; + } + #endif + + // 3 clicks: exit and turn off + else if (event == EV_3clicks) { + blink_once(); + set_state(off_state, 0); + return EVENT_HANDLED; + } + + // 4 clicks: exit and turn on + else if (event == EV_4clicks) { + #if defined(USE_MANUAL_MEMORY) && !defined(USE_MANUAL_MEMORY_TIMER) + // this clause probably isn't used by any configs any more + // but is included just in case someone configures it this way + if (cfg.manual_memory) + set_state(steady_state, cfg.manual_memory); + else + #endif + set_state(steady_state, memorized_level); + return EVENT_HANDLED; + } + + // 4 clicks, but hold last: exit and start at floor + else if (event == EV_click4_hold) { + //blink_once(); + blip(); + // reset button sequence to avoid activating anything in ramp mode + current_event = 0; + // ... and back to ramp mode + set_state(steady_state, 1); + return EVENT_HANDLED; + } + + // 5 clicks: exit and turn on at ceiling level + else if (event == EV_5clicks) { + set_state(steady_state, MAX_LEVEL); + return EVENT_HANDLED; + } + + #if NUM_CHANNEL_MODES > 1 + // 3H: next channel mode + else if (event == EV_click3_hold) { + if (0 == (arg % TICKS_PER_SECOND)) { + // pretend the user clicked 3 times to change channels + return channel_mode_state(EV_3clicks, 0); + } + } + #endif + + ////////// Every action below here is blocked in the (non-Extended) Simple UI ////////// + + #if defined(USE_SIMPLE_UI) && !defined(USE_EXTENDED_SIMPLE_UI) + if (cfg.simple_ui_active) { + return EVENT_NOT_HANDLED; + } + #endif // if simple UI but not extended simple UI + + #if defined(USE_INDICATOR_LED) + // 7 clicks: rotate through indicator LED modes (lockout mode) + else if (event == EV_7clicks) { + #if defined(USE_INDICATOR_LED) + uint8_t mode = cfg.indicator_led_mode >> 2; + #ifdef TICK_DURING_STANDBY + mode = (mode + 1) & 3; + #else + mode = (mode + 1) % 3; + #endif + #ifdef INDICATOR_LED_SKIP_LOW + if (mode == 1) { mode ++; } + #endif + cfg.indicator_led_mode = (mode << 2) + (cfg.indicator_led_mode & 0x03); + // redundant, sleep tick does the same thing + //indicator_led_update(cfg.indicator_led_mode >> 2, arg); + #elif defined(USE_AUX_RGB_LEDS) + #endif + save_config(); + return EVENT_HANDLED; + } + #elif defined(USE_AUX_RGB_LEDS) + // 7 clicks: change RGB aux LED pattern + else if (event == EV_7clicks) { + uint8_t mode = (cfg.rgb_led_lockout_mode >> 4) + 1; + mode = mode % RGB_LED_NUM_PATTERNS; + cfg.rgb_led_lockout_mode = (mode << 4) | (cfg.rgb_led_lockout_mode & 0x0f); + rgb_led_update(cfg.rgb_led_lockout_mode, 0); + save_config(); + blink_once(); + return EVENT_HANDLED; + } + // 7H: change RGB aux LED color + else if (event == EV_click7_hold) { + setting_rgb_mode_now = 1; + if (0 == (arg & 0x3f)) { + uint8_t mode = (cfg.rgb_led_lockout_mode & 0x0f) + 1; + mode = mode % RGB_LED_NUM_COLORS; + cfg.rgb_led_lockout_mode = mode | (cfg.rgb_led_lockout_mode & 0xf0); + //save_config(); + } + rgb_led_update(cfg.rgb_led_lockout_mode, arg); + return EVENT_HANDLED; + } + // 7H, release: save new color + else if (event == EV_click7_hold_release) { + setting_rgb_mode_now = 0; + save_config(); + return EVENT_HANDLED; + } + #endif + + #if defined(USE_EXTENDED_SIMPLE_UI) && defined(USE_SIMPLE_UI) + ////////// Every action below here is blocked in the Extended Simple UI ////////// + if (cfg.simple_ui_active) { + return EVENT_NOT_HANDLED; + } + #endif // if extended simple UI + + #ifdef USE_AUTOLOCK + // 10H: configure the autolock option + else if (event == EV_click10_hold) { + push_state(autolock_config_state, 0); + return EVENT_HANDLED; + } + #endif + + return EVENT_NOT_HANDLED; +} + +#ifdef USE_AUTOLOCK +// set the auto-lock timer to N minutes, where N is the number of clicks +void autolock_config_save(uint8_t step, uint8_t value) { + cfg.autolock_time = value; +} + +uint8_t autolock_config_state(Event event, uint16_t arg) { + return config_state_base(event, arg, 1, autolock_config_save); +} +#endif // #ifdef USE_AUTOLOCK + diff --git a/ui/anduril/lockout-mode.h b/ui/anduril/lockout-mode.h new file mode 100644 index 0000000..c2703a0 --- /dev/null +++ b/ui/anduril/lockout-mode.h @@ -0,0 +1,16 @@ +// lockout-mode.h: Lockout mode for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +// soft lockout +uint8_t lockout_state(Event event, uint16_t arg); + +#ifdef USE_AUTOLOCK +#ifndef DEFAULT_AUTOLOCK_TIME +#define DEFAULT_AUTOLOCK_TIME 0 // autolock time in minutes, 0 = disabled +#endif +uint8_t autolock_config_state(Event event, uint16_t arg); +#endif + diff --git a/ui/anduril/misc.c b/ui/anduril/misc.c new file mode 100644 index 0000000..1b92d6f --- /dev/null +++ b/ui/anduril/misc.c @@ -0,0 +1,42 @@ +// misc.c: Misc extra functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "misc.h" + +/* no longer used +void blink_confirm(uint8_t num) { + uint8_t brightness = actual_level; + uint8_t bump = actual_level + BLINK_BRIGHTNESS; + if (bump > MAX_LEVEL) bump = 0; + for (; num>0; num--) { + set_level(bump); + delay_4ms(10/4); + set_level(brightness); + if (num > 1) { delay_4ms(100/4); } + } +} +*/ + +// make a short, visible pulse +// (either brighter or darker, depending on current brightness) +void blink_once() { + uint8_t brightness = actual_level; + uint8_t bump = brightness + BLINK_BRIGHTNESS; + if (bump > MAX_LEVEL) bump = 0; + + set_level(bump); + delay_4ms(BLINK_ONCE_TIME/4); + set_level(brightness); +} + +// Just go dark for a moment to indicate to user that something happened +void blip() { + uint8_t temp = actual_level; + set_level(0); + delay_4ms(3); + set_level(temp); +} + diff --git a/ui/anduril/misc.h b/ui/anduril/misc.h new file mode 100644 index 0000000..0f2992a --- /dev/null +++ b/ui/anduril/misc.h @@ -0,0 +1,10 @@ +// misc.h: Misc extra functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +//void blink_confirm(uint8_t num); // no longer used +void blink_once(); +void blip(); + diff --git a/ui/anduril/momentary-mode.c b/ui/anduril/momentary-mode.c new file mode 100644 index 0000000..a765142 --- /dev/null +++ b/ui/anduril/momentary-mode.c @@ -0,0 +1,67 @@ +// momentary-mode.c: Momentary mode for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "momentary-mode.h" + +uint8_t momentary_state(Event event, uint16_t arg) { + // init strobe mode, if relevant + #ifdef USE_STROBE_STATE + if ((event == EV_enter_state) && (momentary_mode != 0)) { + strobe_state(event, arg); + } + #endif + + // light up when the button is pressed; go dark otherwise + // button is being held + if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { + momentary_active = 1; + // 0 = ramping, 1 = strobes + if (momentary_mode == 0) { + set_level(memorized_level); + } + return EVENT_HANDLED; + } + // button was released + else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { + momentary_active = 0; + set_level(0); + //go_to_standby = 1; // sleep while light is off + return EVENT_HANDLED; + } + + // Sleep, dammit! (but wait a few seconds first) + // (because standby mode uses such little power that it can interfere + // with exiting via tailcap loosen+tighten unless you leave power + // disconnected for several seconds, so we want to be awake when that + // happens to speed up the process) + else if (event == EV_tick) { + #ifdef USE_STROBE_STATE + if (momentary_active) { + // 0 = ramping, non-zero = strobes + if (momentary_mode != 0) { + return strobe_state(event, arg); + } + } + else { + #endif + if (arg > TICKS_PER_SECOND*5) { // sleep after 5 seconds + go_to_standby = 1; // sleep while light is off + // turn off lighted button + #ifdef USE_INDICATOR_LED + indicator_led(0); + #elif defined(USE_AUX_RGB_LEDS) + rgb_led_update(0, 0); + #endif + } + #ifdef USE_STROBE_STATE + } + #endif + return EVENT_HANDLED; + } + + return EVENT_NOT_HANDLED; +} + diff --git a/ui/anduril/momentary-mode.h b/ui/anduril/momentary-mode.h new file mode 100644 index 0000000..d774801 --- /dev/null +++ b/ui/anduril/momentary-mode.h @@ -0,0 +1,11 @@ +// momentary-mode.h: Momentary mode for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +// momentary / signalling mode +uint8_t momentary_state(Event event, uint16_t arg); +uint8_t momentary_mode = 0; // 0 = ramping, 1 = strobe +uint8_t momentary_active = 0; // boolean, true if active *right now* + diff --git a/ui/anduril/off-mode.c b/ui/anduril/off-mode.c new file mode 100644 index 0000000..0a381b7 --- /dev/null +++ b/ui/anduril/off-mode.c @@ -0,0 +1,384 @@ +// off-mode.c: "Off" mode for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "off-mode.h" + +#ifdef USE_SUNSET_TIMER +#include "sunset-timer.h" +#endif + +// set level smooth maybe +void off_state_set_level(uint8_t level); + + +uint8_t off_state(Event event, uint16_t arg) { + + // turn emitter off when entering state + if (event == EV_enter_state) { + // turn off + off_state_set_level(0); + #ifdef USE_SMOOTH_STEPS + // don't go to sleep while animating + arg |= smooth_steps_in_progress; + #endif + ticks_since_on = 0; + #if NUM_CHANNEL_MODES > 1 + // reset to ramp mode's channel when light turns off + channel_mode = cfg.channel_mode; + #endif + #ifdef USE_INDICATOR_LED + // redundant, sleep tick does the same thing + //indicator_led_update(cfg.indicator_led_mode & 0x03, 0); + #elif defined(USE_AUX_RGB_LEDS) + // redundant, sleep tick does the same thing + //rgb_led_update(cfg.rgb_led_off_mode, 0); + #endif + #ifdef USE_SUNSET_TIMER + sunset_timer = 0; // needs a reset in case previous timer was aborted + #endif + // sleep while off (lower power use) + // (unless delay requested; give the ADC some time to catch up) + if (! arg) { go_to_standby = 1; } + return EVENT_HANDLED; + } + + // go back to sleep eventually if we got bumped but didn't leave "off" state + else if (event == EV_tick) { + if (arg > HOLD_TIMEOUT + #ifdef USE_SMOOTH_STEPS + && (! smooth_steps_in_progress) + #endif + ) { + go_to_standby = 1; + #ifdef USE_INDICATOR_LED + // redundant, sleep tick does the same thing + //indicator_led_update(cfg.indicator_led_mode & 0x03, arg); + #elif defined(USE_AUX_RGB_LEDS) + // redundant, sleep tick does the same thing + //rgb_led_update(cfg.rgb_led_off_mode, arg); + #endif + } + return EVENT_HANDLED; + } + + #if defined(TICK_DURING_STANDBY) + // blink the indicator LED, maybe + else if (event == EV_sleep_tick) { + if (ticks_since_on < 255) ticks_since_on ++; + #ifdef USE_MANUAL_MEMORY_TIMER + // reset to manual memory level when timer expires + if (cfg.manual_memory && + (arg >= (cfg.manual_memory_timer * SLEEP_TICKS_PER_MINUTE))) { + manual_memory_restore(); + } + #endif + #ifdef USE_INDICATOR_LED + indicator_led_update(cfg.indicator_led_mode & 0x03, arg); + #elif defined(USE_AUX_RGB_LEDS) + rgb_led_update(cfg.rgb_led_off_mode, arg); + #endif + + #ifdef USE_AUTOLOCK + // lock the light after being off for N minutes + uint16_t ticks = cfg.autolock_time * SLEEP_TICKS_PER_MINUTE; + if ((cfg.autolock_time > 0) && (arg > ticks)) { + set_state(lockout_state, 0); + } + #endif // ifdef USE_AUTOLOCK + return EVENT_HANDLED; + } + #endif + + #if (B_TIMING_ON == B_PRESS_T) + // hold (initially): go to lowest level (floor), but allow abort for regular click + else if (event == EV_click1_press) { + off_state_set_level(nearest_level(1)); + return EVENT_HANDLED; + } + #endif // B_TIMING_ON == B_PRESS_T + + // hold: go to lowest level + else if (event == EV_click1_hold) { + #if (B_TIMING_ON == B_PRESS_T) + #ifdef MOON_TIMING_HINT + if (arg == 0) { + // let the user know they can let go now to stay at moon + blip(); + } else + #endif + #else // B_RELEASE_T or B_TIMEOUT_T + off_state_set_level(nearest_level(1)); + #endif + #ifdef USE_RAMP_AFTER_MOON_CONFIG + if (cfg.dont_ramp_after_moon) { + return EVENT_HANDLED; + } + #endif + // don't start ramping immediately; + // give the user time to release at moon level + //if (arg >= HOLD_TIMEOUT) { // smaller + if (arg >= (!cfg.ramp_style) * HOLD_TIMEOUT) { // more consistent + set_state(steady_state, 1); + } + return EVENT_HANDLED; + } + + // hold, release quickly: go to lowest level (floor) + else if (event == EV_click1_hold_release) { + set_state(steady_state, 1); + return EVENT_HANDLED; + } + + #if (B_TIMING_ON != B_TIMEOUT_T) + // 1 click (before timeout): go to memorized level, but allow abort for double click + else if (event == EV_click1_release) { + #if defined(USE_MANUAL_MEMORY) && !defined(USE_MANUAL_MEMORY_TIMER) + // this clause probably isn't used by any configs any more + // but is included just in case someone configures it this way + if (cfg.manual_memory) { + manual_memory_restore(); + } + #endif + off_state_set_level(nearest_level(memorized_level)); + return EVENT_HANDLED; + } + #endif // if (B_TIMING_ON != B_TIMEOUT_T) + + // 1 click: regular mode + else if (event == EV_1click) { + #if (B_TIMING_ON != B_TIMEOUT_T) + set_state(steady_state, memorized_level); + #else + // FIXME: B_TIMEOUT_T breaks manual_memory and manual_memory_timer + // (need to duplicate manual mem logic here, probably) + set_state(steady_state, memorized_level); + #endif + return EVENT_HANDLED; + } + + // click, hold: momentary at ceiling or turbo + else if (event == EV_click2_hold) { + ticks_since_on = 0; // momentary turbo is definitely "on" + uint8_t turbo_level; // how bright is "turbo"? + + #if defined(USE_2C_STYLE_CONFIG) // user can choose 2C behavior + uint8_t style_2c = cfg.ramp_2c_style; + #ifdef USE_SIMPLE_UI + // simple UI has its own turbo config + if (cfg.simple_ui_active) style_2c = cfg.ramp_2c_style_simple; + #endif + // 0 = ceiling + // 1+ = full power + if (0 == style_2c) turbo_level = nearest_level(MAX_LEVEL); + else turbo_level = MAX_LEVEL; + #else + // simple UI: ceiling + // full UI: full power + #ifdef USE_SIMPLE_UI + if (cfg.simple_ui_active) turbo_level = nearest_level(MAX_LEVEL); + else + #endif + turbo_level = MAX_LEVEL; + #endif + + off_state_set_level(turbo_level); + return EVENT_HANDLED; + } + else if (event == EV_click2_hold_release) { + off_state_set_level(0); + return EVENT_HANDLED; + } + + // 2 clicks: highest mode (ceiling) + else if (event == EV_2clicks) { + set_state(steady_state, MAX_LEVEL); + return EVENT_HANDLED; + } + + // 3 clicks (initial press): off, to prep for later events + else if (event == EV_click3_press) { + #ifdef USE_SMOOTH_STEPS + // immediately cancel any animations in progress + smooth_steps_in_progress = 0; + #endif + off_state_set_level(0); + return EVENT_HANDLED; + } + + #ifdef USE_BATTCHECK + // 3 clicks: battcheck mode / blinky mode group 1 + else if (event == EV_3clicks) { + set_state(battcheck_state, 0); + return EVENT_HANDLED; + } + #endif + + #ifdef USE_LOCKOUT_MODE + // 4 clicks: soft lockout + else if (event == EV_4clicks) { + blink_once(); + set_state(lockout_state, 0); + return EVENT_HANDLED; + } + #endif + + #if defined(USE_FACTORY_RESET) && defined(USE_SOFT_FACTORY_RESET) + // 13 clicks and hold the last click: invoke factory reset (reboot) + else if (event == EV_click13_hold) { + reboot(); + return EVENT_HANDLED; + } + #endif + + #ifdef USE_VERSION_CHECK + // 15+ clicks: show the version number + else if (event == EV_15clicks) { + set_state(version_check_state, 0); + return EVENT_HANDLED; + } + #endif + + #ifdef USE_SIMPLE_UI + // 10 clicks, but hold last click: turn simple UI off (or configure it) + else if ((event == EV_click10_hold) && (!arg)) { + if (cfg.simple_ui_active) { // turn off simple UI + blink_once(); + cfg.simple_ui_active = 0; + save_config(); + } + else { // configure simple UI ramp + push_state(simple_ui_config_state, 0); + } + return EVENT_HANDLED; + } + + ////////// Every action below here is blocked in the (non-Extended) Simple UI ////////// + + #ifndef USE_EXTENDED_SIMPLE_UI + if (cfg.simple_ui_active) { + return EVENT_NOT_HANDLED; + } + #endif // ifndef USE_EXTENDED_SIMPLE_UI + #endif // ifdef USE_SIMPLE_UI + + // click, click, long-click: strobe mode + #ifdef USE_STROBE_STATE + else if (event == EV_click3_hold) { + set_state(strobe_state, 0); + return EVENT_HANDLED; + } + #elif defined(USE_BORING_STROBE_STATE) + else if (event == EV_click3_hold) { + set_state(boring_strobe_state, 0); + return EVENT_HANDLED; + } + #endif + + #ifdef USE_INDICATOR_LED + // 7 clicks: change indicator LED mode + else if (event == EV_7clicks) { + uint8_t mode = (cfg.indicator_led_mode & 3) + 1; + #ifdef TICK_DURING_STANDBY + mode = mode & 3; + #else + mode = mode % 3; + #endif + #ifdef INDICATOR_LED_SKIP_LOW + if (mode == 1) { mode ++; } + #endif + cfg.indicator_led_mode = (cfg.indicator_led_mode & 0b11111100) | mode; + // redundant, sleep tick does the same thing + //indicator_led_update(cfg.indicator_led_mode & 0x03, arg); + save_config(); + return EVENT_HANDLED; + } + #elif defined(USE_AUX_RGB_LEDS) + // 7 clicks: change RGB aux LED pattern + else if (event == EV_7clicks) { + uint8_t mode = (cfg.rgb_led_off_mode >> 4) + 1; + mode = mode % RGB_LED_NUM_PATTERNS; + cfg.rgb_led_off_mode = (mode << 4) | (cfg.rgb_led_off_mode & 0x0f); + rgb_led_update(cfg.rgb_led_off_mode, 0); + save_config(); + blink_once(); + return EVENT_HANDLED; + } + // 7 clicks (hold last): change RGB aux LED color + else if (event == EV_click7_hold) { + setting_rgb_mode_now = 1; + if (0 == (arg & 0x3f)) { + uint8_t mode = (cfg.rgb_led_off_mode & 0x0f) + 1; + mode = mode % RGB_LED_NUM_COLORS; + cfg.rgb_led_off_mode = mode | (cfg.rgb_led_off_mode & 0xf0); + //save_config(); + } + rgb_led_update(cfg.rgb_led_off_mode, arg); + return EVENT_HANDLED; + } + else if (event == EV_click7_hold_release) { + setting_rgb_mode_now = 0; + save_config(); + return EVENT_HANDLED; + } + #endif // end 7 clicks + + ////////// Every action below here is blocked in the Extended Simple UI ////////// + + #ifdef USE_SIMPLE_UI + #ifdef USE_EXTENDED_SIMPLE_UI + if (cfg.simple_ui_active) { + return EVENT_NOT_HANDLED; + } + #endif // ifdef USE_EXTENDED_SIMPLE_UI + + // 10 clicks: enable simple UI + else if (event == EV_10clicks) { + blink_once(); + cfg.simple_ui_active = 1; + save_config(); + return EVENT_HANDLED; + } + #endif // ifdef USE_SIMPLE_UI + + #ifdef USE_MOMENTARY_MODE + // 5 clicks: momentary mode + else if (event == EV_5clicks) { + blink_once(); + set_state(momentary_state, 0); + return EVENT_HANDLED; + } + #endif + + #ifdef USE_TACTICAL_MODE + // 6 clicks: tactical mode + else if (event == EV_6clicks) { + blink_once(); + set_state(tactical_state, 0); + return EVENT_HANDLED; + } + #endif + + #ifdef USE_GLOBALS_CONFIG + // 9 clicks, but hold last click: configure misc global settings + else if ((event == EV_click9_hold) && (!arg)) { + push_state(globals_config_state, 0); + return EVENT_HANDLED; + } + #endif + + return EVENT_NOT_HANDLED; +} + + +void off_state_set_level(uint8_t level) { + // this pattern gets used a few times, so reduce duplication + #ifdef USE_SMOOTH_STEPS + if (cfg.smooth_steps_style) set_level_smooth(level, 8); + else + #endif + set_level(level); +} + diff --git a/ui/anduril/off-mode.h b/ui/anduril/off-mode.h new file mode 100644 index 0000000..d07fff1 --- /dev/null +++ b/ui/anduril/off-mode.h @@ -0,0 +1,12 @@ +// off-mode.h: "Off" mode for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +// was the light in an "on" mode within the past second or so? +uint8_t ticks_since_on = 0; + +// when the light is "off" or in standby +uint8_t off_state(Event event, uint16_t arg); + diff --git a/ui/anduril/ramp-mode-fsm.h b/ui/anduril/ramp-mode-fsm.h new file mode 100644 index 0000000..edfd6db --- /dev/null +++ b/ui/anduril/ramp-mode-fsm.h @@ -0,0 +1,38 @@ +// ramp-mode-fsm.h: FSM config for ramping functions in Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +// enable FSM's ramping features +#define USE_RAMPING + +// do smooth adjustments when compensating for temperature +#ifdef USE_THERMAL_REGULATION +#define USE_SET_LEVEL_GRADUALLY // isn't used except for thermal adjustments +#endif + +// brightness to use when no memory is set +// FIXME: this is only here to stop an error in fsm-ramping.c, +// which should be fixed by using a different symbol instead +// (like BUTTON_LED_BRIGHT_LEVEL or RAMP_HALFWAY_LEVEL or something) +#ifndef DEFAULT_LEVEL +#define DEFAULT_LEVEL MAX_1x7135 +#endif + +// requires the ability to measure time while "off" +#ifdef USE_MANUAL_MEMORY_TIMER +#define TICK_DURING_STANDBY +#endif + +// ensure the jump start feature gets compiled in if needed +#ifdef DEFAULT_JUMP_START_LEVEL +#define USE_JUMP_START +#endif + +// include an extra config mode for random stuff which doesn't fit elsewhere +#if defined(USE_JUMP_START) || \ + (defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING)) +#define USE_GLOBALS_CONFIG +#endif + diff --git a/ui/anduril/ramp-mode.c b/ui/anduril/ramp-mode.c new file mode 100644 index 0000000..4611b4f --- /dev/null +++ b/ui/anduril/ramp-mode.c @@ -0,0 +1,741 @@ +// ramp-mode.c: Ramping functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "ramp-mode.h" + +#ifdef USE_SUNSET_TIMER +#include "sunset-timer.h" +#endif + +#ifdef USE_SMOOTH_STEPS +#include "smooth-steps.h" +#endif + + +uint8_t steady_state(Event event, uint16_t arg) { + static int8_t ramp_direction = 1; + #if (B_TIMING_OFF == B_RELEASE_T) + // if the user double clicks, we need to abort turning off, + // and this stores the level to return to + static uint8_t level_before_off = 0; + #endif + + #if NUM_CHANNEL_MODES > 1 + channel_mode = cfg.channel_mode; + #endif + + // make sure ramp globals are correct... + // ... but they already are; no need to do it here + //ramp_update_config(); + //nearest_level(1); // same effect, takes less space + + uint8_t mode_min = ramp_floor; + uint8_t mode_max = ramp_ceil; + uint8_t step_size; + if (cfg.ramp_style) { step_size = ramp_discrete_step_size; } + else { step_size = 1; } + + // how bright is "turbo"? + uint8_t turbo_level; + #if defined(USE_2C_STYLE_CONFIG) // user can choose 2C behavior + uint8_t style_2c = cfg.ramp_2c_style; + #ifdef USE_SIMPLE_UI + // simple UI has its own turbo config + if (cfg.simple_ui_active) style_2c = cfg.ramp_2c_style_simple; + #endif + // 0 = no turbo + // 1 = Anduril 1 direct to turbo + // 2 = Anduril 2 direct to ceiling, or turbo if already at ceiling + if (0 == style_2c) turbo_level = mode_max; + else if (1 == style_2c) turbo_level = MAX_LEVEL; + else { + if (memorized_level < mode_max) { turbo_level = mode_max; } + else { turbo_level = MAX_LEVEL; } + } + #elif defined(USE_2C_MAX_TURBO) // Anduril 1 style always + // simple UI: to/from ceiling + // full UI: to/from turbo (Anduril1 behavior) + #ifdef USE_SIMPLE_UI + if (cfg.simple_ui_active) turbo_level = mode_max; + else + #endif + turbo_level = MAX_LEVEL; + #else // Anduril 2 style always + // simple UI: to/from ceiling + // full UI: to/from ceiling if mem < ceiling, + // or to/from turbo if mem >= ceiling + if ((memorized_level < mode_max) + #ifdef USE_SIMPLE_UI + || cfg.simple_ui_active + #endif + ) { turbo_level = mode_max; } + else { turbo_level = MAX_LEVEL; } + #endif + + #ifdef USE_SUNSET_TIMER + // handle the shutoff timer first + uint8_t sunset_active = sunset_timer; // save for comparison + // clock tick + sunset_timer_state(event, arg); + // if the timer was just turned on + if (sunset_timer && (! sunset_active)) { + sunset_timer_orig_level = actual_level; + } + // if the timer just expired, shut off + else if (sunset_active && (! sunset_timer)) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + #endif // ifdef USE_SUNSET_TIMER + + // turn LED on when we first enter the mode + if ((event == EV_enter_state) || (event == EV_reenter_state)) { + #if defined(USE_MOMENTARY_MODE) && defined(USE_STROBE_STATE) + momentary_mode = 0; // 0 = ramping, 1 = strobes + #endif + // if we just got back from config mode, go back to memorized level + if (event == EV_reenter_state) { + arg = memorized_level; + } + // remember this level, unless it's moon or turbo + if ((arg > mode_min) && (arg < mode_max)) + memorized_level = arg; + // use the requested level even if not memorized + arg = nearest_level(arg); + set_level_and_therm_target(arg); + ramp_direction = 1; + return EVENT_HANDLED; + } + #if (B_TIMING_OFF == B_RELEASE_T) + // 1 click (early): off, if configured for early response + else if (event == EV_click1_release) { + level_before_off = actual_level; + set_level_and_therm_target(0); + return EVENT_HANDLED; + } + // 2 clicks (early): abort turning off, if configured for early response + else if (event == EV_click2_press) { + set_level_and_therm_target(level_before_off); + return EVENT_HANDLED; + } + #endif // if (B_TIMING_OFF == B_RELEASE_T) + // 1 click: off + else if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: go to/from highest level + else if (event == EV_2clicks) { + if (actual_level < turbo_level) { + set_level_and_therm_target(turbo_level); + } + else { + set_level_and_therm_target(memorized_level); + } + #ifdef USE_SUNSET_TIMER + reset_sunset_timer(); + #endif + return EVENT_HANDLED; + } + + #ifdef USE_LOCKOUT_MODE + // 4 clicks: shortcut to lockout mode + else if (event == EV_4clicks) { + set_level(0); + set_state(lockout_state, 0); + return EVENT_HANDLED; + } + #endif + + // hold: change brightness (brighter, dimmer) + // click, hold: change brightness (dimmer) + else if ((event == EV_click1_hold) || (event == EV_click2_hold)) { + // ramp infrequently in stepped mode + if (cfg.ramp_style && (arg % HOLD_TIMEOUT != 0)) + return EVENT_HANDLED; + #ifdef USE_RAMP_SPEED_CONFIG + // ramp slower if user configured things that way + if ((! cfg.ramp_style) && (arg % ramp_speed)) + return EVENT_HANDLED; + #endif + #ifdef USE_SMOOTH_STEPS + // if a brightness transition is already happening, + // don't interrupt it + // (like 2C for full turbo then 1H to smooth ramp down + // ... without this clause, it flickers because it trips + // the "blink at ramp ceil" clause below, over and over) + if (smooth_steps_in_progress) return EVENT_HANDLED; + #endif + // fix ramp direction on first frame if necessary + if (!arg) { + // click, hold should always go down if possible + if (event == EV_click2_hold) { ramp_direction = -1; } + // make it ramp down instead, if already at max + else if (actual_level >= mode_max) { ramp_direction = -1; } + // make it ramp up if already at min + // (off->hold->stepped_min->release causes this state) + else if (actual_level <= mode_min) { ramp_direction = 1; } + } + // if the button is stuck, err on the side of safety and ramp down + else if ((arg > TICKS_PER_SECOND * 5 + #ifdef USE_RAMP_SPEED_CONFIG + // FIXME: count from time actual_level hits mode_max, + // not from beginning of button hold + * ramp_speed + #endif + ) && (actual_level >= mode_max)) { + ramp_direction = -1; + } + #ifdef USE_LOCKOUT_MODE + // if the button is still stuck, lock the light + else if ((arg > TICKS_PER_SECOND * 10 + #ifdef USE_RAMP_SPEED_CONFIG + // FIXME: count from time actual_level hits mode_min, + // not from beginning of button hold + * ramp_speed + #endif + ) && (actual_level <= mode_min)) { + blink_once(); + set_state(lockout_state, 0); + } + #endif + memorized_level = nearest_level((int16_t)actual_level \ + + (step_size * ramp_direction)); + #if defined(BLINK_AT_RAMP_CEIL) || defined(BLINK_AT_RAMP_MIDDLE) + // only blink once for each threshold + // FIXME: blinks at beginning of smooth_steps animation instead + // of the end, so it should blink when actual_level reaches a + // threshold, instead of when memorized_level does + // (one possible fix is to just remove mid-ramp blinks entirely, + // and just blink only when it hits the top while going up) + if ((memorized_level != actual_level) && ( + 0 // for easier syntax below + #ifdef BLINK_AT_RAMP_MIDDLE_1 + || (memorized_level == BLINK_AT_RAMP_MIDDLE_1) + #endif + #ifdef BLINK_AT_RAMP_MIDDLE_2 + || (memorized_level == BLINK_AT_RAMP_MIDDLE_2) + #endif + #ifdef BLINK_AT_RAMP_CEIL + // FIXME: only blink at top when going up, not down + || (memorized_level == mode_max) + #endif + #ifdef BLINK_AT_RAMP_FLOOR + || (memorized_level == mode_min) + #endif + )) { + blip(); + } + #endif + #if defined(BLINK_AT_STEPS) + uint8_t foo = cfg.ramp_style; + cfg.ramp_style = 1; + uint8_t nearest = nearest_level((int16_t)actual_level); + cfg.ramp_style = foo; + // only blink once for each threshold + if ((memorized_level != actual_level) && + (cfg.ramp_style == 0) && + (memorized_level == nearest) + ) + { + blip(); + } + #endif + set_level_and_therm_target(memorized_level); + #ifdef USE_SUNSET_TIMER + reset_sunset_timer(); + #endif + return EVENT_HANDLED; + } + // reverse ramp direction on hold release + else if ((event == EV_click1_hold_release) + || (event == EV_click2_hold_release)) { + ramp_direction = -ramp_direction; + #ifdef START_AT_MEMORIZED_LEVEL + save_config_wl(); + #endif + return EVENT_HANDLED; + } + + else if (event == EV_tick) { + // un-reverse after 1 second + if (arg == AUTO_REVERSE_TIME) ramp_direction = 1; + + #ifdef USE_SUNSET_TIMER + // reduce output if shutoff timer is active + if (sunset_timer) { + uint8_t dimmed_level = sunset_timer_orig_level * sunset_timer / sunset_timer_peak; + uint8_t dimmed_level_next = sunset_timer_orig_level * (sunset_timer-1) / sunset_timer_peak; + uint8_t dimmed_level_delta = dimmed_level - dimmed_level_next; + dimmed_level -= dimmed_level_delta * (sunset_ticks/TICKS_PER_SECOND) / 60; + if (dimmed_level < 1) dimmed_level = 1; + + #ifdef USE_SET_LEVEL_GRADUALLY + set_level_gradually(dimmed_level); + target_level = dimmed_level; + #else + set_level_and_therm_target(dimmed_level); + #endif + } + #endif // ifdef USE_SUNSET_TIMER + + #ifdef USE_SET_LEVEL_GRADUALLY + int16_t diff = gradual_target - actual_level; + static uint16_t ticks_since_adjust = 0; + ticks_since_adjust++; + if (diff) { + uint16_t ticks_per_adjust = 256; + if (diff < 0) { + //diff = -diff; + if (actual_level > THERM_FASTER_LEVEL) { + #ifdef THERM_HARD_TURBO_DROP + ticks_per_adjust >>= 2; + #endif + ticks_per_adjust >>= 2; + } + } else { + // rise at half speed + ticks_per_adjust <<= 1; + } + while (diff) { + ticks_per_adjust >>= 1; + //diff >>= 1; + diff /= 2; // because shifting produces weird behavior + } + if (ticks_since_adjust > ticks_per_adjust) + { + gradual_tick(); + ticks_since_adjust = 0; + } + } + #endif // ifdef USE_SET_LEVEL_GRADUALLY + return EVENT_HANDLED; + } + + #ifdef USE_THERMAL_REGULATION + // overheating: drop by an amount proportional to how far we are above the ceiling + else if (event == EV_temperature_high) { + #if 0 + blip(); + #endif + #ifdef THERM_HARD_TURBO_DROP + //if (actual_level > THERM_FASTER_LEVEL) { + if (actual_level == MAX_LEVEL) { + #ifdef USE_SET_LEVEL_GRADUALLY + set_level_gradually(THERM_FASTER_LEVEL); + target_level = THERM_FASTER_LEVEL; + #else + set_level_and_therm_target(THERM_FASTER_LEVEL); + #endif + } else + #endif + if (actual_level > MIN_THERM_STEPDOWN) { + int16_t stepdown = actual_level - arg; + if (stepdown < MIN_THERM_STEPDOWN) stepdown = MIN_THERM_STEPDOWN; + else if (stepdown > MAX_LEVEL) stepdown = MAX_LEVEL; + #ifdef USE_SET_LEVEL_GRADUALLY + set_level_gradually(stepdown); + #else + set_level(stepdown); + #endif + } + return EVENT_HANDLED; + } + // underheating: increase slowly if we're lower than the target + // (proportional to how low we are) + else if (event == EV_temperature_low) { + #if 0 + blip(); + #endif + if (actual_level < target_level) { + //int16_t stepup = actual_level + (arg>>1); + int16_t stepup = actual_level + arg; + if (stepup > target_level) stepup = target_level; + else if (stepup < MIN_THERM_STEPDOWN) stepup = MIN_THERM_STEPDOWN; + #ifdef USE_SET_LEVEL_GRADUALLY + set_level_gradually(stepup); + #else + set_level(stepup); + #endif + } + return EVENT_HANDLED; + } + #ifdef USE_SET_LEVEL_GRADUALLY + // temperature is within target window + // (so stop trying to adjust output) + else if (event == EV_temperature_okay) { + // if we're still adjusting output... stop after the current step + if (gradual_target > actual_level) + gradual_target = actual_level + 1; + else if (gradual_target < actual_level) + gradual_target = actual_level - 1; + return EVENT_HANDLED; + } + #endif // ifdef USE_SET_LEVEL_GRADUALLY + #endif // ifdef USE_THERMAL_REGULATION + + ////////// Every action below here is blocked in the simple UI ////////// + // That is, unless we specifically want to enable 3C for smooth/stepped selection in Simple UI + #if defined(USE_SIMPLE_UI) && !defined(USE_SIMPLE_UI_RAMPING_TOGGLE) + if (cfg.simple_ui_active) { + return EVENT_NOT_HANDLED; + } + #endif + + // 3 clicks: toggle smooth vs discrete ramping + // (and/or 6 clicks when there are multiple channel modes) + // (handle 3C here anyway, when all but 1 mode is disabled) + else if ((event == EV_3clicks) + #if NUM_CHANNEL_MODES > 1 + || (event == EV_6clicks) + ) { + // detect if > 1 channel mode is enabled, + // and if so, fall through so channel mode code can handle it + // otherwise, change the ramp style + if (event == EV_3clicks) { + uint8_t enabled = 0; + for (uint8_t m=0; m 1) + return EVENT_NOT_HANDLED; + } + #else + ) { + #endif + + cfg.ramp_style = !cfg.ramp_style; + save_config(); + #ifdef START_AT_MEMORIZED_LEVEL + save_config_wl(); + #endif + blip(); + memorized_level = nearest_level(actual_level); + set_level_and_therm_target(memorized_level); + #ifdef USE_SUNSET_TIMER + reset_sunset_timer(); + #endif + return EVENT_HANDLED; + } + + // If we allowed 3C in Simple UI, now block further actions + #if defined(USE_SIMPLE_UI) && defined(USE_SIMPLE_UI_RAMPING_TOGGLE) + if (cfg.simple_ui_active) { + return EVENT_NOT_HANDLED; + } + #endif + + // 3H: momentary turbo (on lights with no tint ramping) + // (or 4H when tint ramping is available) + else if ((event == EV_click3_hold) + #ifdef USE_CHANNEL_MODE_ARGS + || (event == EV_click4_hold) + #endif + ) { + #ifdef USE_CHANNEL_MODE_ARGS + // ramp tint if tint exists in this mode + if ((event == EV_click3_hold) + && (channel_has_args(channel_mode))) + return EVENT_NOT_HANDLED; + #endif + if (! arg) { // first frame only, to allow thermal regulation to work + #ifdef USE_2C_STYLE_CONFIG + uint8_t tl = style_2c ? MAX_LEVEL : turbo_level; + set_level_and_therm_target(tl); + #else + set_level_and_therm_target(turbo_level); + #endif + } + return EVENT_HANDLED; + } + else if ((event == EV_click3_hold_release) + #ifdef USE_CHANNEL_MODE_ARGS + || (event == EV_click4_hold_release) + #endif + ) { + #ifdef USE_CHANNEL_MODE_ARGS + // ramp tint if tint exists in this mode + if ((event == EV_click3_hold_release) + && (channel_has_args(channel_mode))) + return EVENT_NOT_HANDLED; + #endif + set_level_and_therm_target(memorized_level); + return EVENT_HANDLED; + } + + #ifdef USE_MOMENTARY_MODE + // 5 clicks: shortcut to momentary mode + else if (event == EV_5clicks) { + set_level(0); + set_state(momentary_state, 0); + return EVENT_HANDLED; + } + #endif + + #ifdef USE_RAMP_CONFIG + // 7H: configure this ramp mode + else if (event == EV_click7_hold) { + push_state(ramp_config_state, 0); + return EVENT_HANDLED; + } + #endif + + #ifdef USE_MANUAL_MEMORY + else if (event == EV_10clicks) { + // turn on manual memory and save current brightness + manual_memory_save(); + save_config(); + blink_once(); + return EVENT_HANDLED; + } + else if (event == EV_click10_hold) { + #ifdef USE_RAMP_EXTRAS_CONFIG + // let user configure a bunch of extra ramp options + push_state(ramp_extras_config_state, 0); + #else // manual mem, but no timer + // turn off manual memory; go back to automatic + if (0 == arg) { + cfg.manual_memory = 0; + save_config(); + blink_once(); + } + #endif + return EVENT_HANDLED; + } + #endif // ifdef USE_MANUAL_MEMORY + + return EVENT_NOT_HANDLED; +} + + +#ifdef USE_RAMP_CONFIG +void ramp_config_save(uint8_t step, uint8_t value) { + + // 0 = smooth ramp, 1 = stepped ramp, 2 = simple UI's ramp + uint8_t style = cfg.ramp_style; + #ifdef USE_SIMPLE_UI + if (current_state == simple_ui_config_state) style = 2; + #endif + + #if defined(USE_SIMPLE_UI) && defined(USE_2C_STYLE_CONFIG) + // simple UI config is weird... + // has some ramp extras after floor/ceil/steps + if (4 == step) { + cfg.ramp_2c_style_simple = value; + } + else + #endif + + // save adjusted value to the correct slot + if (value) { + // ceiling value is inverted + if (step == 2) value = MAX_LEVEL + 1 - value; + + // which option are we configuring? + // TODO? maybe rearrange definitions to avoid the need for this table + // (move all ramp values into a single array?) + uint8_t *steps[] = { cfg.ramp_floors, cfg.ramp_ceils, cfg.ramp_stepss }; + uint8_t *option; + option = steps[step-1]; + option[style] = value; + } +} + +uint8_t ramp_config_state(Event event, uint16_t arg) { + #ifdef USE_RAMP_SPEED_CONFIG + const uint8_t num_config_steps = 3; + #else + uint8_t num_config_steps = cfg.ramp_style + 2; + #endif + return config_state_base(event, arg, + num_config_steps, ramp_config_save); +} + +#ifdef USE_SIMPLE_UI +uint8_t simple_ui_config_state(Event event, uint16_t arg) { + #if defined(USE_2C_STYLE_CONFIG) + #define SIMPLE_UI_NUM_MENU_ITEMS 4 + #else + #define SIMPLE_UI_NUM_MENU_ITEMS 3 + #endif + return config_state_base(event, arg, + SIMPLE_UI_NUM_MENU_ITEMS, + ramp_config_save); +} +#endif +#endif // #ifdef USE_RAMP_CONFIG + +#ifdef USE_RAMP_EXTRAS_CONFIG +void ramp_extras_config_save(uint8_t step, uint8_t value) { + // item 1: disable manual memory, go back to automatic + if (manual_memory_config_step == step) { + cfg.manual_memory = 0; + } + + #ifdef USE_MANUAL_MEMORY_TIMER + // item 2: set manual memory timer duration + // FIXME: should be limited to (65535 / SLEEP_TICKS_PER_MINUTE) + // to avoid overflows or impossibly long timeouts + // (by default, the effective limit is 145, but it allows up to 255) + else if (manual_memory_timer_config_step == step) { + cfg.manual_memory_timer = value; + } + #endif + + #ifdef USE_RAMP_AFTER_MOON_CONFIG + // item 3: ramp up after hold-from-off for moon? + // 0 = yes, ramp after moon + // 1+ = no, stay at moon + else if (dont_ramp_after_moon_config_step == step) { + cfg.dont_ramp_after_moon = value; + } + #endif + + #ifdef USE_2C_STYLE_CONFIG + // item 4: Anduril 1 2C turbo, or Anduril 2 2C ceiling? + // 1 = Anduril 1, 2C turbo + // 2+ = Anduril 2, 2C ceiling + else if (ramp_2c_style_config_step == step) { + cfg.ramp_2c_style = value; + } + #endif + + #ifdef USE_SMOOTH_STEPS + else if (smooth_steps_style_config_step == step) { + cfg.smooth_steps_style = value; + } + #endif +} + +uint8_t ramp_extras_config_state(Event event, uint16_t arg) { + return config_state_base(event, arg, + ramp_extras_config_num_steps - 1, + ramp_extras_config_save); +} +#endif + +#ifdef USE_GLOBALS_CONFIG +void globals_config_save(uint8_t step, uint8_t value) { + if (0) {} + #if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING) + else if (step == tint_style_config_step) { cfg.tint_ramp_style = value; } + #endif + #ifdef USE_JUMP_START + else if (step == jump_start_config_step) { cfg.jump_start_level = value; } + #endif +} + +uint8_t globals_config_state(Event event, uint16_t arg) { + return config_state_base(event, arg, + globals_config_num_steps - 1, + globals_config_save); +} +#endif + +// find the ramp level closest to the target, +// using only the levels which are allowed in the current state +uint8_t nearest_level(int16_t target) { + // using int16_t here saves us a bunch of logic elsewhere, + // by allowing us to correct for numbers < 0 or > 255 in one central place + + // ensure all globals are correct + ramp_update_config(); + + // bounds check + uint8_t mode_min = ramp_floor; + uint8_t mode_max = ramp_ceil; + uint8_t num_steps = cfg.ramp_stepss[1 + #ifdef USE_SIMPLE_UI + + cfg.simple_ui_active + #endif + ]; + // special case for 1-step ramp... use halfway point between floor and ceiling + if (cfg.ramp_style && (1 == num_steps)) { + uint8_t mid = (mode_max + mode_min) >> 1; + return mid; + } + if (target < mode_min) return mode_min; + if (target > mode_max) return mode_max; + // the rest isn't relevant for smooth ramping + if (! cfg.ramp_style) return target; + + uint8_t ramp_range = mode_max - mode_min; + ramp_discrete_step_size = ramp_range / (num_steps-1); + uint8_t this_level = mode_min; + + for(uint8_t i=0; i>1)) + return this_level; + } + return this_level; +} + +// ensure ramp globals are correct +void ramp_update_config() { + uint8_t which = cfg.ramp_style; + #ifdef USE_SIMPLE_UI + if (cfg.simple_ui_active) { which = 2; } + #endif + + ramp_floor = cfg.ramp_floors[which]; + ramp_ceil = cfg.ramp_ceils[which]; +} + +#if defined(USE_THERMAL_REGULATION) || defined(USE_SMOOTH_STEPS) +void set_level_and_therm_target(uint8_t level) { + #ifdef USE_THERMAL_REGULATION + target_level = level; + #endif + #ifdef USE_SMOOTH_STEPS + // if adjusting by more than 1 ramp level, + // animate the step change (if smooth steps enabled) + uint8_t diff = (level > actual_level) + ? (level - actual_level) : (actual_level - level); + if (smooth_steps_in_progress + || (cfg.smooth_steps_style && (diff > 1))) + set_level_smooth(level, 4); + else + #endif + set_level(level); +} +#else +#define set_level_and_therm_target(level) set_level(level) +#endif + +void manual_memory_restore() { + memorized_level = cfg.manual_memory; + #if NUM_CHANNEL_MODES > 1 + channel_mode = cfg.channel_mode = cfg.manual_memory_channel_mode; + #endif + #ifdef USE_CHANNEL_MODE_ARGS + for (uint8_t i=0; i 1 + cfg.manual_memory_channel_mode = channel_mode; + #endif + #ifdef USE_CHANNEL_MODE_ARGS + for (uint8_t i=0; i= 3 + #ifndef BLINK_AT_RAMP_MIDDLE_1 + #define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135 + #ifndef BLINK_AT_RAMP_MIDDLE_2 + #define BLINK_AT_RAMP_MIDDLE_2 MAX_1x7135 + #endif + #endif + #else + #ifndef BLINK_AT_RAMP_MIDDLE_1 + #define BLINK_AT_RAMP_MIDDLE_1 MAX_1x7135 + #endif + #endif +#endif + + +// ramping mode and its related config mode +uint8_t steady_state(Event event, uint16_t arg); + +#ifdef USE_RAMP_CONFIG +uint8_t ramp_config_state(Event event, uint16_t arg); +void ramp_config_save(uint8_t step, uint8_t value); +#ifdef USE_SIMPLE_UI +uint8_t simple_ui_config_state(Event event, uint16_t arg); +#endif +#endif + +#if defined(USE_MANUAL_MEMORY_TIMER) || defined(USE_RAMP_AFTER_MOON_CONFIG) || defined(USE_2C_STYLE_CONFIG) || defined(USE_AUTO_SUNSET) +#define USE_RAMP_EXTRAS_CONFIG +#endif +#ifdef USE_RAMP_EXTRAS_CONFIG +uint8_t ramp_extras_config_state(Event event, uint16_t arg); +#endif + +// calculate the nearest ramp level which would be valid at the moment +// (is a no-op for smooth ramp, but limits discrete ramp to only the +// correct levels for the user's config) +uint8_t nearest_level(int16_t target); + +// ensure ramp globals are correct +void ramp_update_config(); + +#if defined(USE_THERMAL_REGULATION) || defined(USE_SMOOTH_STEPS) +// brightness before thermal step-down +uint8_t target_level = 0; +void set_level_and_therm_target(uint8_t level); +#else +#define set_level_and_therm_target(level) set_level(level) +#endif + + +// brightness control +uint8_t memorized_level = DEFAULT_LEVEL; +#ifdef USE_MANUAL_MEMORY + void manual_memory_restore(); + void manual_memory_save(); + #ifndef DEFAULT_MANUAL_MEMORY + #define DEFAULT_MANUAL_MEMORY 0 + #endif + #ifdef USE_MANUAL_MEMORY_TIMER + #ifndef DEFAULT_MANUAL_MEMORY_TIMER + #define DEFAULT_MANUAL_MEMORY_TIMER 0 + #endif + #endif +#endif + +#ifndef DEFAULT_2C_STYLE_SIMPLE + #define DEFAULT_2C_STYLE_SIMPLE 0 +#endif + +#ifdef USE_2C_STYLE_CONFIG +#ifndef DEFAULT_2C_STYLE +#define DEFAULT_2C_STYLE 2 +#endif + +#ifdef USE_2C_MAX_TURBO +#error Cannot use USE_2C_MAX_TURBO and USE_2C_STYLE_CONFIG at the same time. +#endif +#endif + +#ifdef USE_RAMP_SPEED_CONFIG +#define ramp_speed (cfg.ramp_stepss[0]) +#endif +#ifdef USE_RAMP_AFTER_MOON_CONFIG +#ifndef DEFAULT_DONT_RAMP_AFTER_MOON +#define DEFAULT_DONT_RAMP_AFTER_MOON 0 +#endif +#endif +// current values, regardless of style +uint8_t ramp_floor = RAMP_SMOOTH_FLOOR; +uint8_t ramp_ceil = RAMP_SMOOTH_CEIL; + +uint8_t ramp_discrete_step_size; // don't set this + +#ifdef USE_SUNSET_TIMER +uint8_t sunset_timer_orig_level = 0; +void reset_sunset_timer(); +#endif + +#ifdef USE_RAMP_EXTRAS_CONFIG +typedef enum { + ramp_extras_cfg_zero = 0, + manual_memory_config_step, + #ifdef USE_MANUAL_MEMORY_TIMER + manual_memory_timer_config_step, + #endif + #ifdef USE_RAMP_AFTER_MOON_CONFIG + dont_ramp_after_moon_config_step, + #endif + #ifdef USE_2C_STYLE_CONFIG + ramp_2c_style_config_step, + #endif + #ifdef USE_SMOOTH_STEPS + smooth_steps_style_config_step, + #endif + ramp_extras_config_num_steps +} ramp_extras_config_steps_e; +#endif + +#ifdef USE_GLOBALS_CONFIG +typedef enum { + globals_cfg_zero = 0, + #if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING) + tint_style_config_step, + #endif + #ifdef USE_JUMP_START + jump_start_config_step, + #endif + globals_config_num_steps +} globals_config_steps_e; + +void globals_config_save(uint8_t step, uint8_t value); +uint8_t globals_config_state(Event event, uint16_t arg); +#endif + diff --git a/ui/anduril/smooth-steps.c b/ui/anduril/smooth-steps.c new file mode 100644 index 0000000..9631e41 --- /dev/null +++ b/ui/anduril/smooth-steps.c @@ -0,0 +1,47 @@ +// smooth-steps.c: Smooth step adjustments for Anduril. +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "smooth-steps.h" + +#ifdef USE_SMOOTH_STEPS + +// one iteration of main loop() +void smooth_steps_iter() { + if (actual_level == smooth_steps_target) { + set_level(smooth_steps_target); + smooth_steps_in_progress = 0; + // restore prev_level when animation ends + prev_level = smooth_steps_start; + } + else if (smooth_steps_target > actual_level) { + // power-linear(ish) ascent + // (jump by ~20% of remaining distance on each frame) + uint8_t diff = smooth_steps_target - actual_level; + uint8_t this = diff / smooth_steps_speed; + if (!this) this = 1; + set_level(actual_level + this); + nice_delay_ms(10); + } else { + // ramp-linear descent + // (jump by 1 on each frame, frame rate gives constant total time) + uint8_t diff = smooth_steps_start - smooth_steps_target; + uint16_t delay = 1 + (30 * smooth_steps_speed / diff); + set_level(actual_level - 1); + // TODO? if delay < one PWM cycle, this can look a little weird + nice_delay_ms(delay); + } +} + +void set_level_smooth(uint8_t level, uint8_t speed) { + smooth_steps_target = level; + smooth_steps_speed = speed; // higher = slower + smooth_steps_in_progress = 1; + // for setting prev_level after animation ends + smooth_steps_start = actual_level; +} + +#endif + diff --git a/ui/anduril/smooth-steps.h b/ui/anduril/smooth-steps.h new file mode 100644 index 0000000..a553af2 --- /dev/null +++ b/ui/anduril/smooth-steps.h @@ -0,0 +1,19 @@ +// smooth-steps.h: Smooth step adjustments for Anduril. +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#ifdef USE_SMOOTH_STEPS + +uint8_t smooth_steps_start; +uint8_t smooth_steps_target; +uint8_t smooth_steps_in_progress; +uint8_t smooth_steps_speed; + +void smooth_steps_iter(); + +void set_level_smooth(uint8_t level, uint8_t speed); + +#endif + diff --git a/ui/anduril/sos-mode.c b/ui/anduril/sos-mode.c new file mode 100644 index 0000000..2a4e97c --- /dev/null +++ b/ui/anduril/sos-mode.c @@ -0,0 +1,56 @@ +// sos-mode.c: SOS mode for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "sos-mode.h" + +#ifdef USE_SOS_MODE_IN_BLINKY_GROUP +uint8_t sos_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: next blinky mode + else if (event == EV_2clicks) { + #if defined(USE_BATTCHECK_MODE) + set_state(battcheck_state, 0); + #elif defined(USE_THERMAL_REGULATION) + set_state(tempcheck_state, 0); + #elif defined(USE_BEACON_MODE) + set_state(beacon_state, 0); + #endif + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} +#endif + +void sos_blink(uint8_t num, uint8_t dah) { + #define DIT_LENGTH 200 + for (; num > 0; num--) { + set_level(memorized_level); + nice_delay_ms(DIT_LENGTH); + if (dah) { // dah is 3X as long as a dit + nice_delay_ms(DIT_LENGTH*2); + } + set_level(0); + // one "off" dit between blinks + nice_delay_ms(DIT_LENGTH); + } + // three "off" dits (or one "dah") between letters + // (except for SOS, which is collectively treated as a single "letter") + //nice_delay_ms(DIT_LENGTH*2); +} + +inline void sos_mode_iter() { + // one iteration of main loop() + //nice_delay_ms(1000); + sos_blink(3, 0); // S + sos_blink(3, 1); // O + sos_blink(3, 0); // S + nice_delay_ms(2000); +} + diff --git a/ui/anduril/sos-mode.h b/ui/anduril/sos-mode.h new file mode 100644 index 0000000..5af61be --- /dev/null +++ b/ui/anduril/sos-mode.h @@ -0,0 +1,11 @@ +// sos-mode.h: SOS mode for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#ifdef USE_SOS_MODE_IN_BLINKY_GROUP +// automatic SOS emergency signal +uint8_t sos_state(Event event, uint16_t arg); +#endif + diff --git a/ui/anduril/strobe-modes-fsm.h b/ui/anduril/strobe-modes-fsm.h new file mode 100644 index 0000000..4d948ed --- /dev/null +++ b/ui/anduril/strobe-modes-fsm.h @@ -0,0 +1,55 @@ +// strobe-modes-fsm.h: FSM config for strobe modes in Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +// enable the random number generator if we need it +#if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE) +#define USE_PSEUDO_RAND +#endif + +// party strobe uses really short pulses +#ifdef USE_PARTY_STROBE_MODE +#define USE_DELAY_ZERO +#endif + +// candle mode is basically a bunch of stacked random triangle waves +#if defined(USE_CANDLE_MODE) +#define USE_TRIANGLE_WAVE +#endif + +// the presence of strobe mode(s) affects how many eeprom bytes we need, +// so it's relevant for FSM configuration +#if defined(USE_CANDLE_MODE) || defined(USE_BIKE_FLASHER_MODE) || defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) || defined(USE_LIGHTNING_MODE) +#define USE_STROBE_STATE +#endif + +// internal numbering for strobe modes +#ifdef USE_STROBE_STATE +typedef enum { + #ifdef USE_PARTY_STROBE_MODE + party_strobe_e, + #endif + #ifdef USE_TACTICAL_STROBE_MODE + tactical_strobe_e, + #endif + #ifdef USE_POLICE_COLOR_STROBE_MODE + police_color_strobe_e, + #endif + #ifdef USE_LIGHTNING_MODE + lightning_storm_e, + #endif + #ifdef USE_CANDLE_MODE + candle_mode_e, + #endif + #ifdef USE_BIKE_FLASHER_MODE + bike_flasher_e, + #endif + strobe_mode_END +} strobe_mode_te; + +//const int NUM_STROBES = strobe_mode_END; +#define NUM_STROBES strobe_mode_END +#endif + diff --git a/ui/anduril/strobe-modes.c b/ui/anduril/strobe-modes.c new file mode 100644 index 0000000..ad17964 --- /dev/null +++ b/ui/anduril/strobe-modes.c @@ -0,0 +1,332 @@ +// strobe-modes.c: Strobe modes for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "strobe-modes.h" + +#ifdef USE_STROBE_STATE +uint8_t strobe_state(Event event, uint16_t arg) { + static int8_t ramp_direction = 1; + + // 'st' reduces ROM size slightly + strobe_mode_te st = current_strobe_type; + + #ifdef USE_MOMENTARY_MODE + momentary_mode = 1; // 0 = ramping, 1 = strobes + #endif + + #ifdef USE_CANDLE_MODE + // pass all events to candle mode, when it's active + // (the code is in its own pseudo-state to keep things cleaner) + if (st == candle_mode_e) { + candle_mode_state(event, arg); + } + #endif + + if (0) {} // placeholder + // init anything which needs to be initialized + else if (event == EV_enter_state) { + current_strobe_type = cfg.strobe_type; + ramp_direction = 1; + return EVENT_HANDLED; + } + // 1 click: off + else if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: rotate through strobe/flasher modes + else if (event == EV_2clicks) { + current_strobe_type = cfg.strobe_type = (st + 1) % NUM_STROBES; + save_config(); + return EVENT_HANDLED; + } + #if (NUM_CHANNEL_MODES > 1) && defined(USE_CHANNEL_PER_STROBE) + // 3 clicks: rotate through channel modes for the current strobe + else if (event == EV_3clicks) { + // TODO: maybe skip aux modes? + set_channel_mode((channel_mode + 1) % NUM_CHANNEL_MODES); + cfg.strobe_channels[st] = channel_mode; + save_config(); + return EVENT_HANDLED; + } + #endif + // 4 clicks: rotate backward through strobe/flasher modes + else if (event == EV_4clicks) { + current_strobe_type = cfg.strobe_type = (st - 1 + NUM_STROBES) % NUM_STROBES; + save_config(); + return EVENT_HANDLED; + } + // hold: change speed (go faster) + // or change brightness (brighter) + else if (event == EV_click1_hold) { + if (0) {} // placeholder + + // party / tactical strobe faster + #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) + #ifdef USE_TACTICAL_STROBE_MODE + else if (st <= tactical_strobe_e) { + #else + else if (st == party_strobe_e) { + #endif + if ((arg & 1) == 0) { + uint8_t d = cfg.strobe_delays[st]; + d -= ramp_direction; + if (d < 8) d = 8; + else if (d > 254) d = 254; + cfg.strobe_delays[st] = d; + } + } + #endif + + // lightning has no adjustments + //else if (st == lightning_storm_e) {} + + // biking mode brighter + #ifdef USE_BIKE_FLASHER_MODE + else if (st == bike_flasher_e) { + cfg.bike_flasher_brightness += ramp_direction; + if (cfg.bike_flasher_brightness < 2) cfg.bike_flasher_brightness = 2; + else if (cfg.bike_flasher_brightness > MAX_BIKING_LEVEL) cfg.bike_flasher_brightness = MAX_BIKING_LEVEL; + set_level(cfg.bike_flasher_brightness); + } + #endif + + return EVENT_HANDLED; + } + // reverse ramp direction on hold release + // ... and save new strobe settings + else if (event == EV_click1_hold_release) { + ramp_direction = -ramp_direction; + save_config(); + return EVENT_HANDLED; + } + // click, hold: change speed (go slower) + // or change brightness (dimmer) + else if (event == EV_click2_hold) { + ramp_direction = 1; + + if (0) {} // placeholder + + // party / tactical strobe slower + #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) + #ifdef USE_TACTICAL_STROBE_MODE + else if (st <= tactical_strobe_e) { + #else + else if (st == party_strobe_e) { + #endif + if ((arg & 1) == 0) { + if (cfg.strobe_delays[st] < 255) cfg.strobe_delays[st] ++; + } + } + #endif + + // lightning has no adjustments + //else if (st == lightning_storm_e) {} + + // biking mode dimmer + #ifdef USE_BIKE_FLASHER_MODE + else if (st == bike_flasher_e) { + if (cfg.bike_flasher_brightness > 2) + cfg.bike_flasher_brightness --; + set_level(cfg.bike_flasher_brightness); + } + #endif + + return EVENT_HANDLED; + } + // release hold: save new strobe settings + else if (event == EV_click2_hold_release) { + save_config(); + return EVENT_HANDLED; + } + #ifdef USE_MOMENTARY_MODE + // 5 clicks: go to momentary mode (momentary strobe) + else if (event == EV_5clicks) { + set_state(momentary_state, 0); + set_level(0); + return EVENT_HANDLED; + } + #endif + #if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE) + // clock tick: bump the random seed + else if (event == EV_tick) { + // un-reverse after 1 second + if (arg == AUTO_REVERSE_TIME) ramp_direction = 1; + + pseudo_rand_seed += arg; + return EVENT_HANDLED; + } + #endif + return EVENT_NOT_HANDLED; +} + +// runs repeatedly in FSM loop() whenever UI is in strobe_state or momentary strobe +inline void strobe_state_iter() { + uint8_t st = current_strobe_type; // can't use switch() on an enum + + #if (NUM_CHANNEL_MODES > 1) && defined(USE_CHANNEL_PER_STROBE) + // remember channel mode for each strobe + channel_mode = cfg.strobe_channels[st]; + #endif + + switch(st) { + #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) + #ifdef USE_PARTY_STROBE_MODE + case party_strobe_e: + #endif + #ifdef USE_TACTICAL_STROBE_MODE + case tactical_strobe_e: + #endif + party_tactical_strobe_mode_iter(st); + break; + #endif + + #ifdef USE_POLICE_COLOR_STROBE_MODE + case police_color_strobe_e: + police_color_strobe_iter(); + break; + #endif + + #ifdef USE_LIGHTNING_MODE + case lightning_storm_e: + lightning_storm_iter(); + break; + #endif + + #ifdef USE_BIKE_FLASHER_MODE + case bike_flasher_e: + bike_flasher_iter(); + break; + #endif + } +} +#endif // ifdef USE_STROBE_STATE + +#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) +inline void party_tactical_strobe_mode_iter(uint8_t st) { + // one iteration of main loop() + uint8_t del = cfg.strobe_delays[st]; + // TODO: make tac strobe brightness configurable? + set_level(STROBE_BRIGHTNESS); + if (0) {} // placeholder + #ifdef USE_PARTY_STROBE_MODE + else if (st == party_strobe_e) { // party strobe + #ifdef PARTY_STROBE_ONTIME + nice_delay_ms(PARTY_STROBE_ONTIME); + #else + if (del < 42) delay_zero(); + else nice_delay_ms(1); + #endif + } + #endif + #ifdef USE_TACTICAL_STROBE_MODE + else { //tactical strobe + nice_delay_ms(del >> 1); + } + #endif + set_level(STROBE_OFF_LEVEL); + nice_delay_ms(del); // no return check necessary on final delay +} +#endif + +#ifdef USE_POLICE_COLOR_STROBE_MODE +inline void police_color_strobe_iter() { + // one iteration of main loop() + uint8_t del = 66; + // TODO: make police strobe brightness configurable + uint8_t bright = memorized_level; + //uint8_t channel = channel_mode; + + for (uint8_t i=0; i<10; i++) { + if (0 == i) set_channel_mode(POLICE_COLOR_STROBE_CH1); + else if (5 == i) set_channel_mode(POLICE_COLOR_STROBE_CH2); + set_level(bright); + nice_delay_ms(del >> 1); + set_level(STROBE_OFF_LEVEL); + nice_delay_ms(del); + } + + // restore the channel when done + //set_channel_mode(channel); + channel_mode = cfg.channel_mode; +} +#endif + +#ifdef USE_LIGHTNING_MODE +inline void lightning_storm_iter() { + // one iteration of main loop() + int16_t brightness; + uint16_t rand_time; + + // turn the emitter on at a random level, + // for a random amount of time between 1ms and 32ms + //rand_time = 1 << (pseudo_rand() % 7); + rand_time = pseudo_rand() & 63; + brightness = 1 << (pseudo_rand() % 7); // 1, 2, 4, 8, 16, 32, 64 + brightness += 1 << (pseudo_rand() % 5); // 2 to 80 now + brightness += pseudo_rand() % brightness; // 2 to 159 now (w/ low bias) + if (brightness > MAX_LEVEL) brightness = MAX_LEVEL; + set_level(brightness); + nice_delay_ms(rand_time); + + // decrease the brightness somewhat more gradually, like lightning + uint8_t stepdown = brightness >> 3; + if (stepdown < 1) stepdown = 1; + while(brightness > 1) { + nice_delay_ms(rand_time); + brightness -= stepdown; + if (brightness < 0) brightness = 0; + set_level(brightness); + /* + if ((brightness < MAX_LEVEL/2) && (! (pseudo_rand() & 15))) { + brightness <<= 1; + set_level(brightness); + } + */ + if (! (pseudo_rand() & 3)) { + nice_delay_ms(rand_time); + set_level(brightness>>1); + } + } + + // turn the emitter off, + // for a random amount of time between 1ms and 8192ms + // (with a low bias) + rand_time = 1 << (pseudo_rand() % 13); + rand_time += pseudo_rand() % rand_time; + set_level(0); + nice_delay_ms(rand_time); // no return check necessary on final delay +} +#endif + +#ifdef USE_BIKE_FLASHER_MODE +#ifndef BIKE_STROBE_ONTIME +#define BIKE_STROBE_ONTIME 0 +#endif +inline void bike_flasher_iter() { + // one iteration of main loop() + uint8_t burst = cfg.bike_flasher_brightness << 1; + if (burst > MAX_LEVEL) burst = MAX_LEVEL; + for(uint8_t i=0; i<4; i++) { + set_level(burst); + nice_delay_ms(5 + BIKE_STROBE_ONTIME); + set_level(cfg.bike_flasher_brightness); + nice_delay_ms(65); + } + nice_delay_ms(720); // no return check necessary on final delay + set_level(0); +} +#endif + +#ifdef USE_CANDLE_MODE +#include "candle-mode.c" +#endif + + +#ifdef USE_BORING_STROBE_STATE +#include "ff-strobe-modes.c" +#endif + diff --git a/ui/anduril/strobe-modes.h b/ui/anduril/strobe-modes.h new file mode 100644 index 0000000..7dc1df4 --- /dev/null +++ b/ui/anduril/strobe-modes.h @@ -0,0 +1,71 @@ +// strobe-modes.h: Strobe modes for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#ifdef USE_STROBE_STATE + +strobe_mode_te current_strobe_type; + +// which strobe mode is active? +#ifdef USE_CANDLE_MODE + #define DEFAULT_STROBE candle_mode_e +#else + #define DEFAULT_STROBE 0 +#endif + +#endif // ifdef USE_STROBE_STATE + + +// full FET strobe can be a bit much... use max regulated level instead, +// if there's a bright enough regulated level +#ifndef STROBE_BRIGHTNESS +#ifdef MAX_Nx7135 +#define STROBE_BRIGHTNESS MAX_Nx7135 +#else +#define STROBE_BRIGHTNESS MAX_LEVEL +#endif +#endif + +// some drivers need to keep the regulator chip on between pulses, +// so set this to 1 to keep the light on at moon mode between pulses, +// and thus keep the regulator powered up for the next flash +#ifndef STROBE_OFF_LEVEL +#define STROBE_OFF_LEVEL 0 +#endif + +// party and tactical strobes +#ifdef USE_STROBE_STATE +uint8_t strobe_state(Event event, uint16_t arg); +inline void strobe_state_iter(); +#endif + +#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) +inline void party_tactical_strobe_mode_iter(uint8_t st); +#endif + +#ifdef USE_POLICE_COLOR_STROBE_MODE +inline void police_color_strobe_iter(); +#endif + +#ifdef USE_LIGHTNING_MODE +inline void lightning_storm_iter(); +#endif + +// bike mode config options +#ifdef USE_BIKE_FLASHER_MODE +#define MAX_BIKING_LEVEL 120 // should be 127 or less +inline void bike_flasher_iter(); +#endif + +#ifdef USE_CANDLE_MODE +#include "candle-mode.h" +#endif + + +#if defined(USE_POLICE_STROBE_MODE) || defined(USE_SOS_MODE_IN_FF_GROUP) +#define USE_BORING_STROBE_STATE +#include "ff-strobe-modes.h" +#endif + diff --git a/ui/anduril/sunset-timer.c b/ui/anduril/sunset-timer.c new file mode 100644 index 0000000..e4fc512 --- /dev/null +++ b/ui/anduril/sunset-timer.c @@ -0,0 +1,60 @@ +// sunset-timer.c: Sunset / candle auto-shutoff functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "sunset-timer.h" + +uint8_t sunset_timer_state(Event event, uint16_t arg) { + + #if defined(USE_SIMPLE_UI) && !defined(USE_EXTENDED_SIMPLE_UI) + // No timer functions in Simple UI + if (cfg.simple_ui_active) return EVENT_NOT_HANDLED; + #endif + + // reset on start + if (event == EV_enter_state) { + sunset_timer = 0; + sunset_ticks = 0; + return EVENT_HANDLED; + } + // hold: maybe "bump" the timer if it's active and almost expired + else if (event == EV_hold) { + // ramping up should "bump" the timer to extend the deadline a bit + if ((sunset_timer > 0) && (sunset_timer < 4)) { + sunset_timer = 3; // 3 minutes + sunset_timer_peak = 3; + sunset_ticks = 0; // re-start current "minute" + } + } + // 5H: add 5m to timer, per second, until released + else if (event == EV_click5_hold) { + if (0 == (arg % TICKS_PER_SECOND)) { + if (sunset_timer < (255 - SUNSET_TIMER_UNIT)) { + // add a few minutes to the timer + sunset_timer += SUNSET_TIMER_UNIT; + sunset_timer_peak = sunset_timer; // reset ceiling + sunset_ticks = 0; // reset phase + // let the user know something happened + blink_once(); + } + } + return EVENT_HANDLED; + } + // tick: count down until time expires + else if (event == EV_tick) { + // time passed + sunset_ticks ++; + // did we reach a minute mark? + if (sunset_ticks >= TICKS_PER_MINUTE) { + sunset_ticks = 0; + if (sunset_timer > 0) { + sunset_timer --; + } + } + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + diff --git a/ui/anduril/sunset-timer.h b/ui/anduril/sunset-timer.h new file mode 100644 index 0000000..963804e --- /dev/null +++ b/ui/anduril/sunset-timer.h @@ -0,0 +1,17 @@ +// sunset-timer.h: Sunset / candle auto-shutoff functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +// how many minutes to add each time the user "bumps" the timer? +#define SUNSET_TIMER_UNIT 5 + +#define TICKS_PER_MINUTE (TICKS_PER_SECOND*60) + +// automatic shutoff timer +uint8_t sunset_timer = 0; // minutes remaining in countdown +uint8_t sunset_timer_peak = 0; // total minutes in countdown +uint16_t sunset_ticks = 0; // counts from 0 to TICKS_PER_MINUTE, then repeats +uint8_t sunset_timer_state(Event event, uint16_t arg); + diff --git a/ui/anduril/tactical-mode.c b/ui/anduril/tactical-mode.c new file mode 100644 index 0000000..0035496 --- /dev/null +++ b/ui/anduril/tactical-mode.c @@ -0,0 +1,109 @@ +// tactical-mode.c: Tactical (ish) mode for Anduril. +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "tactical-mode.h" + + +uint8_t tactical_state(Event event, uint16_t arg) { + // momentary(ish) tactical mode + uint8_t mem_lvl = memorized_level; // save this to restore it later + uint8_t ret = EVENT_NOT_HANDLED; + + // button is being held + if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { + // 1H: 1st level + // 2H: 2nd level + // 3H: 3rd level + // 4+: nothing + momentary_active = 0; + ret = EVENT_HANDLED; + uint8_t click = event & 0x0f; // click number + if (click <= 3) { + momentary_active = 1; + uint8_t lvl; + lvl = cfg.tactical_levels[click-1]; + if ((1 <= lvl) && (lvl <= RAMP_SIZE)) { // steady output + memorized_level = lvl; + momentary_mode = 0; + #if NUM_CHANNEL_MODES > 1 + // use ramp mode's channel + channel_mode = cfg.channel_mode; + #endif + } else { // momentary strobe mode + momentary_mode = 1; + if (lvl > RAMP_SIZE) { + current_strobe_type = (lvl - RAMP_SIZE - 1) % strobe_mode_END; + } + } + } + } + // button was released + else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { + momentary_active = 0; + set_level(0); + interrupt_nice_delays(); // stop animations in progress + } + + // delegate to momentary mode while button is pressed + if (momentary_active) { + momentary_state(event, arg); + } + + memorized_level = mem_lvl; // restore temporarily overridden mem level + + // copy lockout mode's aux LED and sleep behaviors + if (event == EV_enter_state) { + lockout_state(event, arg); + } + else if (event == EV_tick) { + if (! momentary_active) { + return lockout_state(event, arg); + } + return EVENT_HANDLED; + } + else if (event == EV_sleep_tick) { + return lockout_state(event, arg); + } + + // 6 clicks: exit and turn off + else if (event == EV_6clicks) { + blink_once(); + set_state(off_state, 0); + return EVENT_HANDLED; + } + + ////////// Every action below here is blocked in the simple UI ////////// + // (unnecessary since this entire mode is blocked in simple UI) + /* + #ifdef USE_SIMPLE_UI + if (cfg.simple_ui_active) { + return EVENT_NOT_HANDLED; + } + #endif + */ + + // 7H: configure tactical mode + else if (event == EV_click7_hold) { + push_state(tactical_config_state, 0); + return EVENT_HANDLED; + } + + return ret; +} + +void tactical_config_save(uint8_t step, uint8_t value) { + // update tac mode values + // 3 values + // each value is 1 to 150, or other: + // - 1..150 is a ramp level + // - other means "strobe mode" + cfg.tactical_levels[step - 1] = value; +} + +uint8_t tactical_config_state(Event event, uint16_t arg) { + return config_state_base(event, arg, 3, tactical_config_save); +} + diff --git a/ui/anduril/tactical-mode.h b/ui/anduril/tactical-mode.h new file mode 100644 index 0000000..528a796 --- /dev/null +++ b/ui/anduril/tactical-mode.h @@ -0,0 +1,22 @@ +// tactical-mode.h: Tactical mode for Anduril. +// Copyright (C) 2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#ifndef TACTICAL_LEVELS + // high, low, tactical strobe + // only do color strobe here if it's main LEDs, not aux LEDs + #if defined(USE_POLICE_COLOR_STROBE_MODE) && !defined(POLICE_STROBE_USES_AUX) + // 2-color police style strobe + #define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) + #else + // regular tactical strobe (1 color) + #define TACTICAL_LEVELS 120,30,(RAMP_SIZE+2) + #endif +#endif + +// tactical(ish) mode +uint8_t tactical_state(Event event, uint16_t arg); +uint8_t tactical_config_state(Event event, uint16_t arg); + diff --git a/ui/anduril/tempcheck-mode.c b/ui/anduril/tempcheck-mode.c new file mode 100644 index 0000000..5d160bd --- /dev/null +++ b/ui/anduril/tempcheck-mode.c @@ -0,0 +1,56 @@ +// tempcheck-mode.c: Temperature check mode for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "tempcheck-mode.h" + +uint8_t tempcheck_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: next blinky mode + else if (event == EV_2clicks) { + #if defined(USE_BEACON_MODE) + set_state(beacon_state, 0); + #elif defined(USE_SOS_MODE) && defined(USE_SOS_MODE_IN_BLINKY_GROUP) + set_state(sos_state, 0); + #elif defined(USE_BATTCHECK) + set_state(battcheck_state, 0); + #endif + return EVENT_HANDLED; + } + // 7H: thermal config mode + else if (event == EV_click7_hold) { + push_state(thermal_config_state, 0); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + +void thermal_config_save(uint8_t step, uint8_t value) { + if (value) { + // item 1: calibrate room temperature + if (step == 1) { + int8_t rawtemp = temperature - cfg.therm_cal_offset; + cfg.therm_cal_offset = value - rawtemp; + adc_reset = 2; // invalidate all recent temperature data + } + + // item 2: set maximum heat limit + else { + cfg.therm_ceil = 30 + value - 1; + } + } + + if (cfg.therm_ceil > MAX_THERM_CEIL) cfg.therm_ceil = MAX_THERM_CEIL; +} + +uint8_t thermal_config_state(Event event, uint16_t arg) { + return config_state_base(event, arg, + 2, thermal_config_save); +} + diff --git a/ui/anduril/tempcheck-mode.h b/ui/anduril/tempcheck-mode.h new file mode 100644 index 0000000..15dd03e --- /dev/null +++ b/ui/anduril/tempcheck-mode.h @@ -0,0 +1,12 @@ +// tempcheck-mode.h: Temperature check mode for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#define USE_BLINK_NUM // FIXME: this only matters in an earlier header + +uint8_t tempcheck_state(Event event, uint16_t arg); +uint8_t thermal_config_state(Event event, uint16_t arg); +void thermal_config_save(uint8_t step, uint8_t value); + diff --git a/ui/anduril/tint-ramping.c b/ui/anduril/tint-ramping.c new file mode 100644 index 0000000..9418113 --- /dev/null +++ b/ui/anduril/tint-ramping.c @@ -0,0 +1,86 @@ +// tint-ramping.c: Tint ramping functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "tint-ramping.h" + +uint8_t tint_ramping_state(Event event, uint16_t arg) { + static int8_t tint_ramp_direction = 1; + static uint8_t prev_tint = 0; + // don't activate auto-tint modes unless the user hits the edge + // and keeps pressing for a while + static uint8_t past_edge_counter = 0; + // bugfix: click-click-hold from off to strobes would invoke tint ramping + // in addition to changing state... so ignore any tint-ramp events which + // don't look like they were meant to be here + static uint8_t active = 0; + + // click, click, hold: change the tint + if (event == EV_click3_hold) { + ///// tint-toggle mode + // toggle once on first frame; ignore other frames + if (tint_style) { + // only respond on first frame + if (arg) return EVENT_NOT_HANDLED; + + // force tint to be 1 or 254 + if (tint != 254) { tint = 1; } + // invert between 1 and 254 + tint = tint ^ 0xFF; + set_level(actual_level); + return EVENT_HANDLED; + } + + ///// smooth tint-ramp mode + // reset at beginning of movement + if (! arg) { + active = 1; // first frame means this is for us + past_edge_counter = 0; // doesn't start until user hits the edge + } + // ignore event if we weren't the ones who handled the first frame + if (! active) return EVENT_HANDLED; + + // change normal tints + if ((tint_ramp_direction > 0) && (tint < 254)) { + tint += 1; + } + else if ((tint_ramp_direction < 0) && (tint > 1)) { + tint -= 1; + } + // if the user kept pressing long enough, go the final step + if (past_edge_counter == 64) { + past_edge_counter ++; + tint ^= 1; // 0 -> 1, 254 -> 255 + blip(); + } + // if tint change stalled, let user know we hit the edge + else if (prev_tint == tint) { + if (past_edge_counter == 0) blip(); + // count up but don't wrap back to zero + if (past_edge_counter < 255) past_edge_counter ++; + } + prev_tint = tint; + set_level(actual_level); + return EVENT_HANDLED; + } + + // click, click, hold, release: reverse direction for next ramp + else if (event == EV_click3_hold_release) { + active = 0; // ignore next hold if it wasn't meant for us + // reverse + tint_ramp_direction = -tint_ramp_direction; + if (tint <= 1) tint_ramp_direction = 1; + else if (tint >= 254) tint_ramp_direction = -1; + // remember tint after battery change + save_config(); + // bug?: for some reason, brightness can seemingly change + // from 1/150 to 2/150 without this next line... not sure why + set_level(actual_level); + return EVENT_HANDLED; + } + + return EVENT_NOT_HANDLED; +} + diff --git a/ui/anduril/tint-ramping.h b/ui/anduril/tint-ramping.h new file mode 100644 index 0000000..19b8dde --- /dev/null +++ b/ui/anduril/tint-ramping.h @@ -0,0 +1,21 @@ +// tint-ramping.h: Tint ramping functions for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +// 0: smooth tint ramp +// 1: toggle tint only between two extremes +#ifdef TINT_RAMP_TOGGLE_ONLY +uint8_t tint_style = 1; +#else +uint8_t tint_style = 0; +#endif + +#ifdef USE_MANUAL_MEMORY +uint8_t manual_memory_tint; +#endif + +// not actually a mode, more of a fallback under other modes +uint8_t tint_ramping_state(Event event, uint16_t arg); + diff --git a/ui/anduril/version-check-mode.c b/ui/anduril/version-check-mode.c new file mode 100644 index 0000000..a47706f --- /dev/null +++ b/ui/anduril/version-check-mode.c @@ -0,0 +1,31 @@ +// version-check-mode.c: Version check mode for Anduril. +// Copyright (C) 2017-2023 Selene ToyKeeper +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "version-check-mode.h" + +// empty state; logic is handled in FSM loop() instead +uint8_t version_check_state(Event event, uint16_t arg) { + return EVENT_NOT_HANDLED; +} + +// this happens in FSM loop() +inline void version_check_iter() { + for (uint8_t i=0; i. + */ + +#include "hwdef-Emisar_D4.h" +#define USE_LVP +#define USE_THERMAL_REGULATION +#define USE_DELAY_MS +#include "spaghetti-monster.h" + +// FSM states +uint8_t off_state(Event event, uint16_t arg); +uint8_t steady_state(Event event, uint16_t arg); +uint8_t lockout_state(Event event, uint16_t arg); + +// brightness control +uint8_t memorized_level = 1; +uint8_t actual_level = 0; +#ifdef USE_THERMAL_REGULATION +uint8_t target_level = 0; +#endif + +// moon + ../../bin/level_calc.py 2 6 7135 18 10 150 FET 1 10 1500 +uint8_t pwm1_levels[] = { 3, 18, 110, 255, 255, 255, 0, }; +uint8_t pwm2_levels[] = { 0, 0, 0, 9, 58, 138, 255, }; +#define MAX_LEVEL (sizeof(pwm1_levels)-1) + +// set LED brightness +void set_level(uint8_t lvl) { + actual_level = lvl; + PWM1_LVL = pwm1_levels[lvl]; + PWM2_LVL = pwm2_levels[lvl]; +} + +uint8_t off_state(Event event, uint16_t arg) { + // turn emitter off when entering state + if (event == EV_enter_state) { + go_to_standby = 1; // sleep while off (lower power use) + return EVENT_HANDLED; + } + // hold (initially): go to lowest level, but allow abort for regular click + else if (event == EV_click1_press) { + set_level(0); + return EVENT_HANDLED; + } + // hold (longer): go to lowest level + else if (event == EV_click1_hold) { + set_state(steady_state, 0); + return EVENT_HANDLED; + } + // 1 click (before timeout): go to memorized level, but allow abort for double click + else if (event == EV_click1_release) { + set_level(memorized_level); + return EVENT_HANDLED; + } + // 1 click: regular mode + else if (event == EV_1click) { + set_state(steady_state, memorized_level); + return EVENT_HANDLED; + } + // 2 clicks: highest mode + else if (event == EV_2clicks) { + set_state(steady_state, MAX_LEVEL); + return EVENT_HANDLED; + } + // 4 clicks: soft lockout + else if (event == EV_4clicks) { + set_state(lockout_state, 0); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + +uint8_t steady_state(Event event, uint16_t arg) { + // turn LED on when we first enter the mode + if (event == EV_enter_state) { + // remember this level, unless it's moon or turbo + if ((arg > 0) && (arg < MAX_LEVEL)) memorized_level = arg; + // use the requested level even if not memorized + #ifdef USE_THERMAL_REGULATION + target_level = arg; + #endif + set_level(arg); + return EVENT_HANDLED; + } + // 1 click: off + else if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: go to/from highest level + else if (event == EV_2clicks) { + if (actual_level < MAX_LEVEL) { // go to turbo + memorized_level = actual_level; // in case we're on moon + #ifdef USE_THERMAL_REGULATION + target_level = MAX_LEVEL; + #endif + set_level(MAX_LEVEL); + } + else { // return from turbo + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #endif + set_level(memorized_level); + } + return EVENT_HANDLED; + } + // hold: change brightness + else if (event == EV_click1_hold) { + if ((arg % HOLD_TIMEOUT) == 0) { + memorized_level = (actual_level+1) % (MAX_LEVEL+1); + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #endif + set_level(memorized_level); + } + return EVENT_HANDLED; + } + #ifdef USE_THERMAL_REGULATION + // overheating: drop by 1 level + else if (event == EV_temperature_high) { + if (actual_level > 1) { set_level(actual_level - 1); } + return EVENT_HANDLED; + } + // underheating: increase by 1 level if we're lower than the target + else if (event == EV_temperature_low) { + if (actual_level < target_level) { set_level(actual_level + 1); } + return EVENT_HANDLED; + } + #endif + return EVENT_NOT_HANDLED; +} + +uint8_t lockout_state(Event event, uint16_t arg) { + // stay asleep while locked + if (event == EV_tick) { + PWM1_LVL = 0; PWM2_LVL = 0; // make sure emitters are off + // sleep 1 second after user stops pressing buttons + if (arg > TICKS_PER_SECOND) { go_to_standby = 1; } + return EVENT_HANDLED; + } + // 4 clicks: exit, and turn on at "low" level + else if (event == EV_4clicks) { + set_state(steady_state, 1); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + +void low_voltage() { + // step down by one level or turn off + if (actual_level > 0) { + set_level(actual_level - 1); + #ifdef USE_THERMAL_REGULATION + target_level = actual_level; // don't let low temperature override LVP + #endif + } + else { + set_state(off_state, 0); + } +} + +void setup() { + // blink when power is connected + set_level(MAX_LEVEL/2); + delay_ms(10); + set_level(0); + + push_state(off_state, 0); +} + +void loop() { +} diff --git a/ui/baton/baton.txt b/ui/baton/baton.txt new file mode 100644 index 0000000..2f0c22f --- /dev/null +++ b/ui/baton/baton.txt @@ -0,0 +1,21 @@ +This is a very simple clone of the Olight Baton interface. It is not +exact, but it has the basics. Mostly, it exists for the purposes of +demonstrating how to create interfaces in FSM. + +While off: + + - 1 click: Turn on (at memorized level). + - Hold: Turn on (at moon level). + - 2 clicks: Turn on (at highest level). + - 4 clicks: Soft lockout mode. + +While on: + + - 1 click: Turn off. + - Hold: Change the brightness. Goes up in steps, then wraps around. + - 2 clicks: Go to/from highest level. + +While locked: + + - 4 clicks: Exit lockout mode. + diff --git a/ui/darkhorse/darkhorse.c b/ui/darkhorse/darkhorse.c new file mode 100644 index 0000000..aa37b92 --- /dev/null +++ b/ui/darkhorse/darkhorse.c @@ -0,0 +1,367 @@ +/* + * DarkHorse: Improved ZebraLight clone UI for SpaghettiMonster. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "hwdef-Emisar_D4.h" +#define USE_LVP +#define USE_THERMAL_REGULATION +#define DEFAULT_THERM_CEIL 45 +#define USE_RAMPING +#define RAMP_LENGTH 150 +#define USE_BATTCHECK +#define BATTCHECK_4bars +#define DONT_DELAY_AFTER_BATTCHECK +#define USE_EEPROM +#define EEPROM_BYTES 5 +#include "spaghetti-monster.h" + +// FSM states +uint8_t off_state(Event event, uint16_t arg); +uint8_t low_mode_state(Event event, uint16_t arg); +uint8_t med_mode_state(Event event, uint16_t arg); +uint8_t hi_mode_state(Event event, uint16_t arg); +uint8_t strobe_beacon_state(Event event, uint16_t arg); +#ifdef USE_BATTCHECK +uint8_t battcheck_state(Event event, uint16_t arg); +#endif +// Not a FSM state, just handles stuff common to all low/med/hi states +uint8_t any_mode_state(Event event, uint16_t arg, uint8_t *primary, uint8_t *secondary, uint8_t *modes); + +void load_config(); +void save_config(); + +// toggle between L1/L2, M1/M2, H1/H2 +uint8_t L1 = 1; +uint8_t M1 = 1; +uint8_t H1 = 1; +// brightness for L2, M2, H2 (valid range 1 to 3 inclusive) +uint8_t L2 = 1; +uint8_t M2 = 1; +uint8_t H2 = 1; +// mode groups, ish +uint8_t low_modes[] = {12, 3, 5, 9}; // 3.3 lm, 2.0 lm, 0.8 lm, 0.3 lm +uint8_t med_modes[] = {56, 21, 29, 37}; // 101 lm, 35 lm, 20 lm, 10 lm +uint8_t hi_modes[] = {MAX_LEVEL, 81, 96, 113}; // 1500 lm, 678 lm, 430 lm, 270 lm +// strobe/beacon modes: +// 0: 0.2 Hz beacon at L1 +// 1: 0.2 Hz beacon at H1 +// 2: 4 Hz strobe at H1 +// 3: 19 Hz strobe at H1 +uint8_t strobe_beacon_mode = 0; + +#ifdef USE_THERMAL_REGULATION +// brightness before thermal step-down +uint8_t target_level = 0; +#endif + +void set_any_mode(uint8_t primary, uint8_t secondary, uint8_t *modes) { + // primary (H1/M1/L1) + if (primary) { + set_level(modes[0]); + } + // secondary (H2/M2/L2) + else { + set_level(modes[secondary]); + } + #ifdef USE_THERMAL_REGULATION + target_level = actual_level; + #endif +} + +inline void set_low_mode() { set_any_mode(L1, L2, low_modes); } +inline void set_med_mode() { set_any_mode(M1, M2, med_modes); } +inline void set_hi_mode() { set_any_mode(H1, H2, hi_modes); } + + +uint8_t off_state(Event event, uint16_t arg) { + // turn emitter off when entering state + if (event == EV_enter_state) { + set_level(0); + // sleep while off (lower power use) + go_to_standby = 1; + return EVENT_HANDLED; + } + // hold (initially): go to lowest level, but allow abort for regular click + else if (event == EV_click1_press) { + set_low_mode(); + return EVENT_HANDLED; + } + // 1 click (before timeout): go to high level, but allow abort for double click + else if (event == EV_click1_release) { + set_hi_mode(); + return EVENT_HANDLED; + } + // 1 click: high mode + else if (event == EV_1click) { + set_state(hi_mode_state, 0); + return EVENT_HANDLED; + } + // click, press (initially): go to medium mode, but allow abort + else if (event == EV_click2_press) { + set_med_mode(); + return EVENT_HANDLED; + } + // 2 clicks: medium mode + else if (event == EV_2clicks) { + set_state(med_mode_state, 0); + return EVENT_HANDLED; + } + // click, click, press (initially): light off, prep for blinkies + else if (event == EV_click3_press) { + set_level(0); + return EVENT_HANDLED; + } + // 3 clicks: strobe mode + else if (event == EV_3clicks) { + set_state(strobe_beacon_state, 0); + return EVENT_HANDLED; + } + #ifdef USE_BATTCHECK + // 4 clicks: battcheck mode + else if (event == EV_4clicks) { + set_state(battcheck_state, 0); + return EVENT_HANDLED; + } + #endif + // hold: go to low mode, but allow ramping up + else if (event == EV_click1_hold) { + // don't start ramping immediately; + // give the user time to release at low mode + if (arg >= HOLD_TIMEOUT) + set_state(low_mode_state, 0); + return EVENT_HANDLED; + } + // hold, release quickly: go to low mode + else if (event == EV_click1_hold_release) { + set_state(low_mode_state, 0); + return EVENT_HANDLED; + } + /* TODO: implement + // click-release-hold: discrete ramp through all levels + else if (event == EV_click2_hold) { + set_state(steady_state, MAX_LEVEL); + return EVENT_HANDLED; + } + */ + return EVENT_NOT_HANDLED; +} + + +uint8_t any_mode_state(Event event, uint16_t arg, uint8_t *primary, uint8_t *secondary, uint8_t *modes) { + // turn on LED when entering the mode + if (event == EV_enter_state) { + set_any_mode(*primary, *secondary, modes); + return EVENT_HANDLED; + } + // 1 click: off + else if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // hold: change brightness (low, med, hi, always starting at low) + else if (event == EV_click1_hold) { + uint8_t which = arg % (HOLD_TIMEOUT * 3) / HOLD_TIMEOUT; + switch(which) { + case 0: + set_state(low_mode_state, 0); + break; + case 1: + set_state(med_mode_state, 0); + break; + case 2: + set_state(hi_mode_state, 0); + break; + } + return EVENT_HANDLED; + } + // 2 clicks: toggle primary/secondary level + else if (event == EV_2clicks) { + *primary ^= 1; + set_any_mode(*primary, *secondary, modes); + save_config(); + return EVENT_HANDLED; + } + // click-release-hold: change secondary level + else if (event == EV_click2_hold) { + if (arg % HOLD_TIMEOUT == 0) { + *secondary = (*secondary + 1) & 3; + if (! *secondary) *secondary = 1; + *primary = 0; + set_any_mode(*primary, *secondary, modes); + } + return EVENT_HANDLED; + } + // click, hold, release: save secondary level + else if (event == EV_click2_hold_release) { + save_config(); + } + #ifdef USE_THERMAL_REGULATION + // TODO: test this on a real light + // overheating: drop by an amount proportional to how far we are above the ceiling + else if (event == EV_temperature_high) { + if (actual_level > MAX_LEVEL/4) { + uint8_t stepdown = actual_level - arg; + if (stepdown < MAX_LEVEL/4) stepdown = MAX_LEVEL/4; + set_level(stepdown); + } + return EVENT_HANDLED; + } + // underheating: increase slowly if we're lower than the target + // (proportional to how low we are) + else if (event == EV_temperature_low) { + if (actual_level < target_level) { + uint8_t stepup = actual_level + (arg>>1); + if (stepup > target_level) stepup = target_level; + set_level(stepup); + } + return EVENT_HANDLED; + } + #endif + return EVENT_NOT_HANDLED; +} + +uint8_t low_mode_state(Event event, uint16_t arg) { + return any_mode_state(event, arg, &L1, &L2, low_modes); +} + +uint8_t med_mode_state(Event event, uint16_t arg) { + return any_mode_state(event, arg, &M1, &M2, med_modes); +} + +uint8_t hi_mode_state(Event event, uint16_t arg) { + return any_mode_state(event, arg, &H1, &H2, hi_modes); +} + + +#ifdef USE_BATTCHECK +uint8_t battcheck_state(Event event, uint16_t arg) { + return EVENT_NOT_HANDLED; +} +#endif + + +uint8_t strobe_beacon_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 1 click (initially): cancel current blink + // FIXME: this is no longer necessary; FSM does this automatically now + if (event == EV_click1_release) { + interrupt_nice_delays(); + return EVENT_HANDLED; + } + // 2 clicks: rotate through blinky modes + else if (event == EV_2clicks) { + strobe_beacon_mode = (strobe_beacon_mode + 1) & 3; + save_config(); + interrupt_nice_delays(); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + + +void low_voltage() { + if (current_state == hi_mode_state) { + set_state(med_mode_state, 0); + } + else if (current_state == med_mode_state) { + set_state(low_mode_state, 0); + } + else if (current_state == low_mode_state) { + set_state(off_state, 0); + } + // "step down" from blinkies to low + else if (current_state == strobe_beacon_state) { + set_state(low_mode_state, 0); + } +} + +void strobe(uint8_t level, uint16_t ontime, uint16_t offtime) { + set_level(level); + if (! nice_delay_ms(ontime)) return; + set_level(0); + nice_delay_ms(offtime); +} + +void load_config() { + if (load_eeprom()) { + H1 = !(!(eeprom[0] & 0b00000100)); + M1 = !(!(eeprom[0] & 0b00000010)); + L1 = !(!(eeprom[0] & 0b00000001)); + H2 = eeprom[1]; + M2 = eeprom[2]; + L2 = eeprom[3]; + strobe_beacon_mode = eeprom[4]; + } +} + +void save_config() { + eeprom[0] = (H1<<2) | (M1<<1) | (L1); + eeprom[1] = H2; + eeprom[2] = M2; + eeprom[3] = L2; + eeprom[4] = strobe_beacon_mode; + + save_eeprom(); +} + +void setup() { + set_level(RAMP_SIZE/8); + delay_4ms(3); + set_level(0); + + load_config(); + + push_state(off_state, 0); +} + +void loop() { + if (current_state == strobe_beacon_state) { + switch(strobe_beacon_mode) { + // 0.2 Hz beacon at L1 + case 0: + strobe(low_modes[0], 500, 4500); + break; + // 0.2 Hz beacon at H1 + case 1: + strobe(hi_modes[0], 500, 4500); + break; + // 4 Hz tactical strobe at H1 + case 2: + strobe(hi_modes[0], 83, 167); + break; + // 19 Hz tactical strobe at H1 + case 3: + strobe(hi_modes[0], 17, 35); + break; + } + } + + #ifdef USE_BATTCHECK + else if (current_state == battcheck_state) { + nice_delay_ms(500); // wait a moment to measure voltage + battcheck(); + set_state(off_state, 0); + } + #endif +} + + diff --git a/ui/fireflies-ui/Makefile b/ui/fireflies-ui/Makefile new file mode 100644 index 0000000..0b59898 --- /dev/null +++ b/ui/fireflies-ui/Makefile @@ -0,0 +1,7 @@ +all: + ./build-all.sh + +clean: + rm -f *.hex cfg-ff-[pr]*.h *~ *.elf *.o + +.phony: clean diff --git a/ui/fireflies-ui/build-all.sh b/ui/fireflies-ui/build-all.sh new file mode 100755 index 0000000..81ebd97 --- /dev/null +++ b/ui/fireflies-ui/build-all.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +cp -av --no-clobber ../anduril/cfg-ff*.h . + +UI=fireflies-ui + +for TARGET in cfg-*.h ; do + NAME=$(echo "$TARGET" | perl -ne '/cfg-(.*).h/ && print "$1\n";') + echo "===== $NAME =====" + echo ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}" + ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}" + mv -f "$UI".hex "$UI".$NAME.hex +done diff --git a/ui/fireflies-ui/cfg-ff-e01.h b/ui/fireflies-ui/cfg-ff-e01.h new file mode 100644 index 0000000..42c23b2 --- /dev/null +++ b/ui/fireflies-ui/cfg-ff-e01.h @@ -0,0 +1,44 @@ +// Fireflies EDC thrower config options for Fireflies UI +// (uses PL47 driver) +#include "hwdef-FF_PL47.h" + +// disable indicator LED; it's hardwired +#ifdef USE_INDICATOR_LED +#undef USE_INDICATOR_LED +#endif + +// don't do this +#undef BLINK_AT_RAMP_MIDDLE +#undef BLINK_AT_RAMP_CEILING + +// ramp shape and size +#define RAMP_LENGTH 150 + +// driver is a FET + 3x7135, ~413 lm at highest regulated level +// ../../../bin/level_calc.py seventh 2 150 7135 1 12 414 FET 2 10 1930 +#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12,13,14,15,15,16,17,18,20,21,22,23,24,26,27,28,30,31,33,34,36,38,39,41,43,45,47,49,51,53,56,58,60,63,65,68,71,74,77,80,83,86,89,93,96,100,103,107,111,115,119,124,128,132,137,142,147,152,157,163,168,174,180,186,192,198,204,211,218,225,232,240,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 +#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,7,9,12,14,16,19,22,24,27,30,33,36,39,42,45,48,52,55,58,62,66,69,73,77,81,85,90,94,98,103,107,112,117,122,127,132,137,143,148,154,160,166,172,178,184,191,197,204,211,218,225,232,240,247,255 +#define MAX_1x7135 93 +#define HALFSPEED_LEVEL 14 +#define QUARTERSPEED_LEVEL 7 + +#define MIN_THERM_STEPDOWN 65 // lowest value it'll step down to + + +// ceiling is level 130/150 (50% power) +#define RAMP_SMOOTH_FLOOR 1 +#define RAMP_SMOOTH_CEIL 130 + +// 20, 56, [93], 130 (93 is highest regulated) +// (8 / 102 / 413 / 1163 + 1930 lm) +#define RAMP_DISCRETE_FLOOR 20 +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#define RAMP_DISCRETE_STEPS 4 + +// ~25 lm to ~400 lm +#define MUGGLE_FLOOR 30 +#define MUGGLE_CEILING MAX_1x7135 + +// throttle back faster when high +#define THERM_FASTER_LEVEL 130 + diff --git a/ui/fireflies-ui/cfg-ff-e07-2.h b/ui/fireflies-ui/cfg-ff-e07-2.h new file mode 100644 index 0000000..48f9c15 --- /dev/null +++ b/ui/fireflies-ui/cfg-ff-e07-2.h @@ -0,0 +1,27 @@ +// Fireflies E07-2 config options for Anduril / FFUI +// mostly the same as PL47 +#include "cfg-ff-pl47.h" + +// ceiling is level 130/150 (50% power) +#undef RAMP_SMOOTH_CEIL +#define RAMP_SMOOTH_CEIL 130 + +// 20, 56, 93, 130 (83 is highest regulated) +// (requested config is 1%, 5%, 25%, 50%, double-click-turbo) +// (but this doesn't allow us to hit level 83) +#undef RAMP_DISCRETE_FLOOR +#define RAMP_DISCRETE_FLOOR 20 +#undef RAMP_DISCRETE_CEIL +#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#undef RAMP_DISCRETE_STEPS +#define RAMP_DISCRETE_STEPS 4 + +// regulate down faster when the FET is active, slower otherwise +#undef THERM_FASTER_LEVEL +#define THERM_FASTER_LEVEL 130 // throttle back faster when high + +// play it safe, don't try to regulate above the recommended safe level +#ifndef THERM_HARD_TURBO_DROP +#define THERM_HARD_TURBO_DROP +#endif + diff --git a/ui/fireflies-ui/fireflies-ui.c b/ui/fireflies-ui/fireflies-ui.c new file mode 100644 index 0000000..34f8293 --- /dev/null +++ b/ui/fireflies-ui/fireflies-ui.c @@ -0,0 +1,2386 @@ +/* + * Fireflies UI: A custom UI for Fireflies-brand flashlights. + * (based on Anduril by ToyKeeper) + * + * Copyright (C) 2019 Selene ToyKeeper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/********* User-configurable options *********/ +// UI config file name (set it here or define it at the gcc command line) +//#define CONFIGFILE cfg-ff-pl47.h + +#define USE_LVP // FIXME: won't build when this option is turned off + +// parameters for this defined below or per-driver +#define USE_THERMAL_REGULATION +#define DEFAULT_THERM_CEIL 45 // try not to get hotter than this + +// short blip when crossing from "click" to "hold" from off +// (helps the user hit moon mode exactly, instead of holding too long +// or too short) +#define MOON_TIMING_HINT +// short blips while ramping +#define BLINK_AT_RAMP_MIDDLE +//#define BLINK_AT_RAMP_FLOOR +#define BLINK_AT_RAMP_CEILING +//#define BLINK_AT_STEPS // whenever a discrete ramp mode is passed in smooth mode + +// ramp down via regular button hold if a ramp-up ended <1s ago +// ("hold, release, hold" ramps down instead of up) +#define USE_REVERSING + +// battery readout style (pick one) +#define BATTCHECK_VpT +//#define BATTCHECK_8bars // FIXME: breaks build +//#define BATTCHECK_4bars // FIXME: breaks build + +// enable/disable various strobe modes +#define USE_BIKE_FLASHER_MODE +#define USE_PARTY_STROBE_MODE +#define USE_TACTICAL_STROBE_MODE +#define USE_LIGHTNING_MODE +#define USE_CANDLE_MODE + +// enable sunset (goodnight) mode +#define USE_GOODNIGHT_MODE +#define GOODNIGHT_TIME 60 // minutes (approximately) +#define GOODNIGHT_LEVEL 24 // ~11 lm + +// enable beacon mode +#define USE_BEACON_MODE + +//Muggle mode for easy UI +#define USE_MUGGLE_MODE + +// make the ramps configurable by the user +#define USE_RAMP_CONFIG + +// boring strobes nobody really likes, but sometimes flashlight companies want +// (these replace the fun strobe group, +// so don't enable them at the same time as any of the above strobes) +//#define USE_POLICE_STROBE_MODE +//#define USE_SOS_MODE + +// dual-switch support (second switch is a tail clicky) +//#define START_AT_MEMORIZED_LEVEL + +/***** specific settings for known driver types *****/ +#include "tk.h" +#include incfile(CONFIGFILE) + +///// Fireflies-specific configuration +// disable ramp config +#ifdef USE_RAMP_CONFIG +#undef USE_RAMP_CONFIG +#endif + +// no muggle mode +#ifdef USE_MUGGLE_MODE +#undef USE_MUGGLE_MODE +#endif + +// turn off strobe mode entirely; we're replacing it +#ifdef USE_BIKE_FLASHER_MODE +#undef USE_BIKE_FLASHER_MODE +#endif +#ifdef USE_PARTY_STROBE_MODE +#undef USE_PARTY_STROBE_MODE +#endif +#ifdef USE_TACTICAL_STROBE_MODE +#undef USE_TACTICAL_STROBE_MODE +#endif +#ifdef USE_LIGHTNING_MODE +#undef USE_LIGHTNING_MODE +#endif +#ifdef USE_CANDLE_MODE +#undef USE_CANDLE_MODE +#endif + +// remove other blinkies too +#ifdef USE_GOODNIGHT_MODE +#undef USE_GOODNIGHT_MODE +#endif +#ifdef USE_BEACON_MODE +#undef USE_BEACON_MODE +#endif + +// use these strobes instead +#define USE_POLICE_STROBE_MODE +#define USE_SOS_MODE + +// thermal config mode on 10 clicks from off +#define USE_TENCLICK_THERMAL_CONFIG + +///// end Fireflies-specific configuration + +// thermal properties, if not defined per-driver +#ifndef MIN_THERM_STEPDOWN +#define MIN_THERM_STEPDOWN MAX_1x7135 // lowest value it'll step down to +#endif +#ifndef THERM_FASTER_LEVEL + #ifdef MAX_Nx7135 + #define THERM_FASTER_LEVEL MAX_Nx7135 // throttle back faster when high + #else + #define THERM_FASTER_LEVEL (RAMP_SIZE*4/5) // throttle back faster when high + #endif +#endif +#ifdef USE_THERMAL_REGULATION +#define USE_SET_LEVEL_GRADUALLY // isn't used except for thermal adjustments +#endif + + +/********* Configure SpaghettiMonster *********/ +#define USE_DELAY_ZERO +#define USE_RAMPING +#ifndef RAMP_LENGTH +#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file +#endif +#define MAX_BIKING_LEVEL 120 // should be 127 or less +#define USE_BATTCHECK + +#if defined(USE_MUGGLE_MODE) +#ifndef MUGGLE_FLOOR +#define MUGGLE_FLOOR 22 +#endif +#ifndef MUGGLE_CEILING +#define MUGGLE_CEILING (MAX_1x7135+20) +#endif +#endif +#define USE_IDLE_MODE // reduce power use while awake and no tasks are pending +#define USE_DYNAMIC_UNDERCLOCKING // cut clock speed at very low modes for better efficiency + +// full FET strobe can be a bit much... use max regulated level instead, +// if there's a bright enough regulated level +#ifdef MAX_Nx7135 +#define STROBE_BRIGHTNESS MAX_Nx7135 +#else +#define STROBE_BRIGHTNESS MAX_LEVEL +#endif + +#if defined(USE_CANDLE_MODE) || defined(USE_BIKE_FLASHER_MODE) || defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) || defined(USE_LIGHTNING_MODE) +#define USE_STROBE_STATE +#endif + +#if defined(USE_POLICE_STROBE_MODE) || defined(USE_SOS_MODE) +#define USE_BORING_STROBE_STATE +#endif + +// auto-detect how many eeprom bytes +#define USE_EEPROM +typedef enum { + ramp_style_e, + #ifdef USE_RAMP_CONFIG + ramp_smooth_floor_e, + ramp_smooth_ceil_e, + ramp_discrete_floor_e, + ramp_discrete_ceil_e, + ramp_discrete_steps_e, + #endif + #ifdef USE_TINT_RAMPING + tint_e, + #endif + #ifdef USE_STROBE_STATE + strobe_type_e, + #endif + #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) + strobe_delays_0_e, + strobe_delays_1_e, + #endif + #ifdef USE_BIKE_FLASHER_MODE + bike_flasher_brightness_e, + #endif + #ifdef USE_BEACON_MODE + beacon_seconds_e, + #endif + #ifdef USE_MUGGLE_MODE + muggle_mode_active_e, + #endif + #ifdef USE_THERMAL_REGULATION + therm_ceil_e, + therm_cal_offset_e, + #endif + #ifdef USE_INDICATOR_LED + indicator_led_mode_e, + #endif + eeprom_indexes_e_END +} eeprom_indexes_e; +#define EEPROM_BYTES eeprom_indexes_e_END + +#ifdef START_AT_MEMORIZED_LEVEL +#define USE_EEPROM_WL +#define EEPROM_WL_BYTES 1 +#endif + +// auto-configure other stuff... +#if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE) +#define USE_PSEUDO_RAND +#endif + +#if defined(USE_CANDLE_MODE) +#ifndef USE_TRIANGLE_WAVE +#define USE_TRIANGLE_WAVE +#endif +#endif + +#include "spaghetti-monster.h" + + +// FSM states +uint8_t off_state(Event event, uint16_t arg); +// simple numeric entry config menu +uint8_t config_state_base(Event event, uint16_t arg, + uint8_t num_config_steps, + void (*savefunc)()); +#define MAX_CONFIG_VALUES 3 +uint8_t config_state_values[MAX_CONFIG_VALUES]; +// ramping mode and its related config mode +uint8_t steady_state(Event event, uint16_t arg); +#ifdef USE_RAMP_CONFIG +uint8_t ramp_config_state(Event event, uint16_t arg); +#endif +#ifdef USE_TINT_RAMPING +// not actually a mode, more of a fallback under other modes +uint8_t tint_ramping_state(Event event, uint16_t arg); +#endif +// party and tactical strobes +#ifdef USE_STROBE_STATE +uint8_t strobe_state(Event event, uint16_t arg); +#endif +#ifdef USE_BORING_STROBE_STATE +uint8_t boring_strobe_state(Event event, uint16_t arg); +volatile uint8_t boring_strobe_type = 0; +void sos_blink(uint8_t num, uint8_t dah); +#define strobe_state boring_strobe_state // use the right strobes +#define NUM_BORING_STROBES 2 +#endif +#ifdef USE_BATTCHECK +uint8_t battcheck_state(Event event, uint16_t arg); +#endif +#ifdef USE_THERMAL_REGULATION +uint8_t tempcheck_state(Event event, uint16_t arg); +uint8_t thermal_config_state(Event event, uint16_t arg); +#endif +#ifdef USE_GOODNIGHT_MODE +// 1-hour ramp down from low, then automatic off +uint8_t goodnight_state(Event event, uint16_t arg); +#endif +#ifdef USE_BEACON_MODE +// beacon mode and its related config mode +uint8_t beacon_state(Event event, uint16_t arg); +uint8_t beacon_config_state(Event event, uint16_t arg); +#endif +// soft lockout +#define MOON_DURING_LOCKOUT_MODE +// if enabled, 2nd lockout click goes to the other ramp's floor level +#define LOCKOUT_MOON_FANCY +uint8_t lockout_state(Event event, uint16_t arg); +// momentary / signalling mode +uint8_t momentary_state(Event event, uint16_t arg); +uint8_t momentary_mode = 0; // 0 = ramping, 1 = strobe +uint8_t momentary_active = 0; // boolean, true if active *right now* +#ifdef USE_MUGGLE_MODE +// muggle mode, super-simple, hard to exit +uint8_t muggle_state(Event event, uint16_t arg); +uint8_t muggle_mode_active = 0; +#endif + +// general helper function for config modes +uint8_t number_entry_state(Event event, uint16_t arg); +// return value from number_entry_state() +volatile uint8_t number_entry_value; + +void blink_confirm(uint8_t num); +void blip(); +#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY) +void indicator_blink(uint8_t arg); +#endif + +// remember stuff even after battery was changed +void load_config(); +void save_config(); +#ifdef START_AT_MEMORIZED_LEVEL +void save_config_wl(); +#endif + +// default ramp options if not overridden earlier per-driver +#ifndef RAMP_STYLE +#define RAMP_STYLE 0 // smooth default +#endif +#ifndef RAMP_SMOOTH_FLOOR + #define RAMP_SMOOTH_FLOOR 1 +#endif +#ifndef RAMP_SMOOTH_CEIL + #if PWM_CHANNELS == 3 + #define RAMP_SMOOTH_CEIL MAX_Nx7135 + #else + #define RAMP_SMOOTH_CEIL MAX_LEVEL - 30 + #endif +#endif +#ifndef RAMP_DISCRETE_FLOOR + #define RAMP_DISCRETE_FLOOR 20 +#endif +#ifndef RAMP_DISCRETE_CEIL + #define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#endif +#ifndef RAMP_DISCRETE_STEPS + #define RAMP_DISCRETE_STEPS 7 +#endif + +// mile marker(s) partway up the ramp +// default: blink only at border between regulated and FET +#ifdef BLINK_AT_RAMP_MIDDLE + #if PWM_CHANNELS >= 3 + #ifndef BLINK_AT_RAMP_MIDDLE_1 + #define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135 + #ifndef BLINK_AT_RAMP_MIDDLE_2 + #define BLINK_AT_RAMP_MIDDLE_2 MAX_1x7135 + #endif + #endif + #else + #ifndef BLINK_AT_RAMP_MIDDLE_1 + #define BLINK_AT_RAMP_MIDDLE_1 MAX_1x7135 + #endif + #endif +#endif + +// brightness control +#ifndef DEFAULT_LEVEL +#define DEFAULT_LEVEL MAX_1x7135 +#endif +uint8_t memorized_level = DEFAULT_LEVEL; +// smooth vs discrete ramping +volatile uint8_t ramp_style = RAMP_STYLE; // 0 = smooth, 1 = discrete +volatile uint8_t ramp_smooth_floor = RAMP_SMOOTH_FLOOR; +volatile uint8_t ramp_smooth_ceil = RAMP_SMOOTH_CEIL; +volatile uint8_t ramp_discrete_floor = RAMP_DISCRETE_FLOOR; +volatile uint8_t ramp_discrete_ceil = RAMP_DISCRETE_CEIL; +volatile uint8_t ramp_discrete_steps = RAMP_DISCRETE_STEPS; +uint8_t ramp_discrete_step_size; // don't set this + +#ifdef USE_INDICATOR_LED + // bits 2-3 control lockout mode + // bits 0-1 control "off" mode + // modes are: 0=off, 1=low, 2=high, 3=blinking (if TICK_DURING_STANDBY enabled) + #ifdef INDICATOR_LED_DEFAULT_MODE + uint8_t indicator_led_mode = INDICATOR_LED_DEFAULT_MODE; + #else + #ifdef USE_INDICATOR_LED_WHILE_RAMPING + //uint8_t indicator_led_mode = (1<<2) + 2; + uint8_t indicator_led_mode = (2<<2) + 1; + #else + uint8_t indicator_led_mode = (3<<2) + 1; + #endif + #endif +#endif + +// calculate the nearest ramp level which would be valid at the moment +// (is a no-op for smooth ramp, but limits discrete ramp to only the +// correct levels for the user's config) +uint8_t nearest_level(int16_t target); + +#ifdef USE_THERMAL_REGULATION +// brightness before thermal step-down +uint8_t target_level = 0; +#endif + +// internal numbering for strobe modes +#ifdef USE_STROBE_STATE +typedef enum { + #ifdef USE_PARTY_STROBE_MODE + party_strobe_e, + #endif + #ifdef USE_TACTICAL_STROBE_MODE + tactical_strobe_e, + #endif + #ifdef USE_LIGHTNING_MODE + lightning_storm_e, + #endif + #ifdef USE_CANDLE_MODE + candle_mode_e, + #endif + #ifdef USE_BIKE_FLASHER_MODE + bike_flasher_e, + #endif + strobe_mode_END +} strobe_mode_te; + +const int NUM_STROBES = strobe_mode_END; + +// which strobe mode is active? +#ifdef USE_CANDLE_MODE +volatile strobe_mode_te strobe_type = candle_mode_e; +#else +volatile strobe_mode_te strobe_type = 0; +#endif +#endif + +#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) +// party / tactical strobe timing +volatile uint8_t strobe_delays[] = { 40, 67 }; // party strobe, tactical strobe +#endif + +// bike mode config options +#ifdef USE_BIKE_FLASHER_MODE +volatile uint8_t bike_flasher_brightness = MAX_1x7135; +#endif + +#ifdef USE_CANDLE_MODE +uint8_t candle_mode_state(Event event, uint16_t arg); +uint8_t triangle_wave(uint8_t phase); +#ifndef CANDLE_AMPLITUDE +#define CANDLE_AMPLITUDE 25 +#endif +#endif + +#ifdef USE_BEACON_MODE +// beacon timing +volatile uint8_t beacon_seconds = 2; +#endif + + +uint8_t off_state(Event event, uint16_t arg) { + // turn emitter off when entering state + if (event == EV_enter_state) { + set_level(0); + #ifdef USE_INDICATOR_LED + indicator_led(indicator_led_mode & 0x03); + #endif + // sleep while off (lower power use) + go_to_standby = 1; + return EVENT_HANDLED; + } + // go back to sleep eventually if we got bumped but didn't leave "off" state + else if (event == EV_tick) { + if (arg > TICKS_PER_SECOND*2) { + go_to_standby = 1; + #ifdef USE_INDICATOR_LED + indicator_led(indicator_led_mode & 0x03); + #endif + } + return EVENT_HANDLED; + } + #if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED) + // blink the indicator LED, maybe + else if (event == EV_sleep_tick) { + if ((indicator_led_mode & 0b00000011) == 0b00000011) { + indicator_blink(arg); + } + return EVENT_HANDLED; + } + #endif + // hold (initially): go to lowest level (floor), but allow abort for regular click + else if (event == EV_click1_press) { + set_level(nearest_level(1)); + return EVENT_HANDLED; + } + // hold: go to lowest level + else if (event == EV_click1_hold) { + #ifdef MOON_TIMING_HINT + if (arg == 0) { + // let the user know they can let go now to stay at moon + blip(); + } else + #endif + // don't start ramping immediately; + // give the user time to release at moon level + //if (arg >= HOLD_TIMEOUT) { // smaller + if (arg >= (!ramp_style) * HOLD_TIMEOUT) { // more consistent + set_state(steady_state, 1); + } + return EVENT_HANDLED; + } + // hold, release quickly: go to lowest level (floor) + else if (event == EV_click1_hold_release) { + set_state(steady_state, 1); + return EVENT_HANDLED; + } + // 1 click (before timeout): go to memorized level, but allow abort for double click + else if (event == EV_click1_release) { + set_level(nearest_level(memorized_level)); + return EVENT_HANDLED; + } + // 1 click: regular mode + else if (event == EV_1click) { + set_state(steady_state, memorized_level); + return EVENT_HANDLED; + } + // click, hold: go to highest level (ceiling) (for ramping down) + else if (event == EV_click2_hold) { + set_state(steady_state, MAX_LEVEL); + return EVENT_HANDLED; + } + // 2 clicks: highest mode (ceiling) + else if (event == EV_2clicks) { + set_state(steady_state, MAX_LEVEL); + return EVENT_HANDLED; + } + // 3 clicks (initial press): off, to prep for later events + else if (event == EV_click3_press) { + set_level(0); + return EVENT_HANDLED; + } + #ifdef USE_BATTCHECK + // 3 clicks: battcheck mode / blinky mode group 1 + else if (event == EV_3clicks) { + set_state(battcheck_state, 0); + return EVENT_HANDLED; + } + #endif + // click, click, long-click: strobe mode + #ifdef USE_STROBE_STATE + else if (event == EV_click3_hold) { + set_state(strobe_state, 0); + return EVENT_HANDLED; + } + #elif defined(USE_BORING_STROBE_STATE) + else if (event == EV_click3_hold) { + set_state(boring_strobe_state, 0); + return EVENT_HANDLED; + } + #endif + // 4 clicks: soft lockout + else if (event == EV_4clicks) { + blink_confirm(2); + set_state(lockout_state, 0); + return EVENT_HANDLED; + } + // 5 clicks: momentary mode + else if (event == EV_5clicks) { + blink_confirm(1); + set_state(momentary_state, 0); + return EVENT_HANDLED; + } + #ifdef USE_MUGGLE_MODE + // 6 clicks: muggle mode + else if (event == EV_6clicks) { + blink_confirm(1); + set_state(muggle_state, 0); + return EVENT_HANDLED; + } + #endif + #ifdef USE_INDICATOR_LED + // 7 clicks: change indicator LED mode + else if (event == EV_7clicks) { + uint8_t mode = (indicator_led_mode & 3) + 1; + #ifdef TICK_DURING_STANDBY + mode = mode & 3; + #else + mode = mode % 3; + #endif + #ifdef INDICATOR_LED_SKIP_LOW + if (mode == 1) { mode ++; } + #endif + indicator_led_mode = (indicator_led_mode & 0b11111100) | mode; + indicator_led(mode); + save_config(); + return EVENT_HANDLED; + } + #endif + // 8 clicks: temperature check + else if (event == EV_8clicks) { + set_state(tempcheck_state, 0); + return EVENT_HANDLED; + } + #ifdef USE_TENCLICK_THERMAL_CONFIG + // 10 clicks: thermal config mode + else if (event == EV_10clicks) { + push_state(thermal_config_state, 0); + return EVENT_HANDLED; + } + #endif + return EVENT_NOT_HANDLED; +} + + +uint8_t steady_state(Event event, uint16_t arg) { + uint8_t mode_min = ramp_smooth_floor; + uint8_t mode_max = ramp_smooth_ceil; + uint8_t ramp_step_size = 1; + #ifdef USE_REVERSING + static int8_t ramp_direction = 1; + #endif + if (ramp_style) { + mode_min = ramp_discrete_floor; + mode_max = ramp_discrete_ceil; + ramp_step_size = ramp_discrete_step_size; + } + + // turn LED on when we first enter the mode + if ((event == EV_enter_state) || (event == EV_reenter_state)) { + momentary_mode = 0; // 0 = ramping, 1 = strobes + // if we just got back from config mode, go back to memorized level + if (event == EV_reenter_state) { + arg = memorized_level; + } + // remember this level, unless it's moon or turbo + if ((arg > mode_min) && (arg < mode_max)) + memorized_level = arg; + // use the requested level even if not memorized + arg = nearest_level(arg); + #ifdef USE_THERMAL_REGULATION + target_level = arg; + #endif + set_level(arg); + #ifdef USE_REVERSING + ramp_direction = 1; + #endif + return EVENT_HANDLED; + } + // 1 click: off + else if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: go to/from highest level + else if (event == EV_2clicks) { + if (actual_level < MAX_LEVEL) { + #ifdef USE_THERMAL_REGULATION + target_level = MAX_LEVEL; + #endif + // true turbo, not the mode-specific ceiling + set_level(MAX_LEVEL); + } + else { + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #endif + set_level(memorized_level); + } + return EVENT_HANDLED; + } + // 3 clicks: toggle smooth vs discrete ramping + else if (event == EV_3clicks) { + ramp_style = !ramp_style; + memorized_level = nearest_level(actual_level); + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #ifdef USE_SET_LEVEL_GRADUALLY + //set_level_gradually(lvl); + #endif + #endif + save_config(); + #ifdef START_AT_MEMORIZED_LEVEL + save_config_wl(); + #endif + blip(); + set_level(memorized_level); + return EVENT_HANDLED; + } + #ifdef USE_RAMP_CONFIG + // 4 clicks: configure this ramp mode + else if (event == EV_4clicks) { + push_state(ramp_config_state, 0); + return EVENT_HANDLED; + } + #endif + // hold: change brightness (brighter) + else if (event == EV_click1_hold) { + // ramp slower in discrete mode + if (ramp_style && (arg % HOLD_TIMEOUT != 0)) { + return EVENT_HANDLED; + } + #ifdef USE_REVERSING + // fix ramp direction on first frame if necessary + if (!arg) { + // make it ramp down instead, if already at max + if (actual_level >= mode_max) { ramp_direction = -1; } + // make it ramp up if already at min + // (off->hold->stepped_min->release causes this state) + else if (actual_level <= mode_min) { ramp_direction = 1; } + } + memorized_level = nearest_level((int16_t)actual_level \ + + (ramp_step_size * ramp_direction)); + #else + memorized_level = nearest_level((int16_t)actual_level + ramp_step_size); + #endif + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #endif + #if defined(BLINK_AT_RAMP_CEILING) || defined(BLINK_AT_RAMP_MIDDLE) + // only blink once for each threshold + if ((memorized_level != actual_level) && ( + 0 // for easier syntax below + #ifdef BLINK_AT_RAMP_MIDDLE_1 + || (memorized_level == BLINK_AT_RAMP_MIDDLE_1) + #endif + #ifdef BLINK_AT_RAMP_MIDDLE_2 + || (memorized_level == BLINK_AT_RAMP_MIDDLE_2) + #endif + #ifdef BLINK_AT_RAMP_CEILING + || (memorized_level == mode_max) + #endif + #if defined(USE_REVERSING) && defined(BLINK_AT_RAMP_FLOOR) + || (memorized_level == mode_min) + #endif + )) { + blip(); + } + #endif + #if defined(BLINK_AT_STEPS) + uint8_t foo = ramp_style; + ramp_style = 1; + uint8_t nearest = nearest_level((int16_t)actual_level); + ramp_style = foo; + // only blink once for each threshold + if ((memorized_level != actual_level) && + (ramp_style == 0) && + (memorized_level == nearest) + ) + { + blip(); + } + #endif + set_level(memorized_level); + return EVENT_HANDLED; + } + #if defined(USE_REVERSING) || defined(START_AT_MEMORIZED_LEVEL) + // reverse ramp direction on hold release + else if (event == EV_click1_hold_release) { + #ifdef USE_REVERSING + ramp_direction = -ramp_direction; + #endif + #ifdef START_AT_MEMORIZED_LEVEL + save_config_wl(); + #endif + return EVENT_HANDLED; + } + #endif + // click, hold: change brightness (dimmer) + else if (event == EV_click2_hold) { + #ifdef USE_REVERSING + ramp_direction = 1; + #endif + // ramp slower in discrete mode + if (ramp_style && (arg % HOLD_TIMEOUT != 0)) { + return EVENT_HANDLED; + } + // TODO? make it ramp up instead, if already at min? + memorized_level = nearest_level((int16_t)actual_level - ramp_step_size); + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #endif + #if defined(BLINK_AT_RAMP_FLOOR) || defined(BLINK_AT_RAMP_MIDDLE) + // only blink once for each threshold + if ((memorized_level != actual_level) && ( + 0 // for easier syntax below + #ifdef BLINK_AT_RAMP_MIDDLE_1 + || (memorized_level == BLINK_AT_RAMP_MIDDLE_1) + #endif + #ifdef BLINK_AT_RAMP_MIDDLE_2 + || (memorized_level == BLINK_AT_RAMP_MIDDLE_2) + #endif + #ifdef BLINK_AT_RAMP_FLOOR + || (memorized_level == mode_min) + #endif + )) { + blip(); + } + #endif + #if defined(BLINK_AT_STEPS) + uint8_t foo = ramp_style; + ramp_style = 1; + uint8_t nearest = nearest_level((int16_t)actual_level); + ramp_style = foo; + // only blink once for each threshold + if ((memorized_level != actual_level) && + (ramp_style == 0) && + (memorized_level == nearest) + ) + { + blip(); + } + #endif + set_level(memorized_level); + return EVENT_HANDLED; + } + #ifdef START_AT_MEMORIZED_LEVEL + // click, release, hold, release: save new ramp level (if necessary) + else if (event == EV_click2_hold_release) { + save_config_wl(); + return EVENT_HANDLED; + } + #endif + #if defined(USE_SET_LEVEL_GRADUALLY) || defined(USE_REVERSING) + else if (event == EV_tick) { + #ifdef USE_REVERSING + // un-reverse after 1 second + if (arg == TICKS_PER_SECOND) ramp_direction = 1; + #endif + #ifdef USE_SET_LEVEL_GRADUALLY + // make thermal adjustment speed scale with magnitude + if ((arg & 1) && (actual_level < THERM_FASTER_LEVEL)) { + return EVENT_HANDLED; // adjust slower when not a high mode + } + #ifdef THERM_HARD_TURBO_DROP + else if ((! (actual_level < THERM_FASTER_LEVEL)) + && (actual_level > gradual_target)) { + gradual_tick(); + } + else { + #endif + // [int(62*4 / (x**0.8)) for x in (1,2,4,8,16,32,64,128)] + //uint8_t intervals[] = {248, 142, 81, 46, 26, 15, 8, 5}; + // [int(62*4 / (x**0.9)) for x in (1,2,4,8,16,32,64,128)] + //uint8_t intervals[] = {248, 132, 71, 38, 20, 10, 5, 3}; + // [int(62*4 / (x**0.95)) for x in (1,2,4,8,16,32,64,128)] + uint8_t intervals[] = {248, 128, 66, 34, 17, 9, 4, 2}; + uint8_t diff; + static uint8_t ticks_since_adjust = 0; + ticks_since_adjust ++; + if (gradual_target > actual_level) diff = gradual_target - actual_level; + else { + diff = actual_level - gradual_target; + } + uint8_t magnitude = 0; + #ifndef THERM_HARD_TURBO_DROP + // if we're on a really high mode, drop faster + if (actual_level >= THERM_FASTER_LEVEL) { magnitude ++; } + #endif + while (diff) { + magnitude ++; + diff >>= 1; + } + uint8_t ticks_per_adjust = intervals[magnitude]; + if (ticks_since_adjust > ticks_per_adjust) + { + gradual_tick(); + ticks_since_adjust = 0; + } + //if (!(arg % ticks_per_adjust)) gradual_tick(); + #ifdef THERM_HARD_TURBO_DROP + } + #endif + #endif + return EVENT_HANDLED; + } + #endif + #ifdef USE_THERMAL_REGULATION + // overheating: drop by an amount proportional to how far we are above the ceiling + else if (event == EV_temperature_high) { + #if 0 + blip(); + #endif + #ifdef THERM_HARD_TURBO_DROP + if (actual_level > THERM_FASTER_LEVEL) { + #ifdef USE_SET_LEVEL_GRADUALLY + set_level_gradually(THERM_FASTER_LEVEL); + #else + set_level(THERM_FASTER_LEVEL); + #endif + target_level = THERM_FASTER_LEVEL; + } else + #endif + if (actual_level > MIN_THERM_STEPDOWN) { + int16_t stepdown = actual_level - arg; + if (stepdown < MIN_THERM_STEPDOWN) stepdown = MIN_THERM_STEPDOWN; + else if (stepdown > MAX_LEVEL) stepdown = MAX_LEVEL; + #ifdef USE_SET_LEVEL_GRADUALLY + set_level_gradually(stepdown); + #else + set_level(stepdown); + #endif + } + return EVENT_HANDLED; + } + // underheating: increase slowly if we're lower than the target + // (proportional to how low we are) + else if (event == EV_temperature_low) { + #if 0 + blip(); + #endif + if (actual_level < target_level) { + //int16_t stepup = actual_level + (arg>>1); + int16_t stepup = actual_level + arg; + if (stepup > target_level) stepup = target_level; + else if (stepup < MIN_THERM_STEPDOWN) stepup = MIN_THERM_STEPDOWN; + #ifdef USE_SET_LEVEL_GRADUALLY + set_level_gradually(stepup); + #else + set_level(stepup); + #endif + } + return EVENT_HANDLED; + } + #endif + return EVENT_NOT_HANDLED; +} + + +#ifdef USE_TINT_RAMPING +uint8_t tint_ramping_state(Event event, uint16_t arg) { + static int8_t tint_ramp_direction = 1; + static uint8_t prev_tint = 0; + // don't activate auto-tint modes unless the user hits the edge + // and keeps pressing for a while + static uint8_t past_edge_counter = 0; + // bugfix: click-click-hold from off to strobes would invoke tint ramping + // in addition to changing state... so ignore any tint-ramp events which + // don't look like they were meant to be here + static uint8_t active = 0; + + // click, click, hold: change the tint + if (event == EV_click3_hold) { + // reset at beginning of movement + if (! arg) { + active = 1; // first frame means this is for us + past_edge_counter = 0; // doesn't start until user hits the edge + } + // ignore event if we weren't the ones who handled the first frame + if (! active) return EVENT_HANDLED; + + // change normal tints + if ((tint_ramp_direction > 0) && (tint < 254)) { + tint += 1; + } + else if ((tint_ramp_direction < 0) && (tint > 1)) { + tint -= 1; + } + // if the user kept pressing long enough, go the final step + if (past_edge_counter == 64) { + past_edge_counter ++; + tint ^= 1; // 0 -> 1, 254 -> 255 + blip(); + } + // if tint change stalled, let user know we hit the edge + else if (prev_tint == tint) { + if (past_edge_counter == 0) blip(); + // count up but don't wrap back to zero + if (past_edge_counter < 255) past_edge_counter ++; + } + prev_tint = tint; + set_level(actual_level); + return EVENT_HANDLED; + } + + // click, click, hold, release: reverse direction for next ramp + else if (event == EV_click3_hold_release) { + active = 0; // ignore next hold if it wasn't meant for us + // reverse + tint_ramp_direction = -tint_ramp_direction; + if (tint == 0) tint_ramp_direction = 1; + else if (tint == 255) tint_ramp_direction = -1; + // remember tint after battery change + save_config(); + return EVENT_HANDLED; + } + + return EVENT_NOT_HANDLED; +} +#endif // ifdef USE_TINT_RAMPING + + +#ifdef USE_STROBE_STATE +uint8_t strobe_state(Event event, uint16_t arg) { + static int8_t ramp_direction = 1; + + // 'st' reduces ROM size by avoiding access to a volatile var + // (maybe I should just make it nonvolatile?) + strobe_mode_te st = strobe_type; + + momentary_mode = 1; // 0 = ramping, 1 = strobes + + #ifdef USE_CANDLE_MODE + // pass all events to candle mode, when it's active + // (the code is in its own pseudo-state to keep things cleaner) + if (st == candle_mode_e) { + candle_mode_state(event, arg); + } + #endif + + if (0) {} // placeholder + // init anything which needs to be initialized + else if (event == EV_enter_state) { + ramp_direction = 1; + return EVENT_HANDLED; + } + // 1 click: off + else if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: rotate through strobe/flasher modes + else if (event == EV_2clicks) { + strobe_type = (st + 1) % NUM_STROBES; + save_config(); + return EVENT_HANDLED; + } + // hold: change speed (go faster) + // or change brightness (brighter) + else if (event == EV_click1_hold) { + if (0) {} // placeholder + + // party / tactical strobe faster + #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) + #ifdef USE_TACTICAL_STROBE_MODE + else if (st <= tactical_strobe_e) { + #else + else if (st == party_strobe_e) { + #endif + if ((arg & 1) == 0) { + uint8_t d = strobe_delays[st]; + d -= ramp_direction; + if (d < 8) d = 8; + else if (d > 254) d = 254; + strobe_delays[st] = d; + } + } + #endif + + // lightning has no adjustments + //else if (st == lightning_storm_e) {} + + // biking mode brighter + #ifdef USE_BIKE_FLASHER_MODE + else if (st == bike_flasher_e) { + bike_flasher_brightness += ramp_direction; + if (bike_flasher_brightness < 2) bike_flasher_brightness = 2; + else if (bike_flasher_brightness > MAX_BIKING_LEVEL) bike_flasher_brightness = MAX_BIKING_LEVEL; + set_level(bike_flasher_brightness); + } + #endif + + return EVENT_HANDLED; + } + // reverse ramp direction on hold release + // ... and save new strobe settings + else if (event == EV_click1_hold_release) { + ramp_direction = -ramp_direction; + save_config(); + return EVENT_HANDLED; + } + // click, hold: change speed (go slower) + // or change brightness (dimmer) + else if (event == EV_click2_hold) { + ramp_direction = 1; + + if (0) {} // placeholder + + // party / tactical strobe slower + #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) + #ifdef USE_TACTICAL_STROBE_MODE + else if (st <= tactical_strobe_e) { + #else + else if (st == party_strobe_e) { + #endif + if ((arg & 1) == 0) { + if (strobe_delays[st] < 255) strobe_delays[st] ++; + } + } + #endif + + // lightning has no adjustments + //else if (st == lightning_storm_e) {} + + // biking mode dimmer + #ifdef USE_BIKE_FLASHER_MODE + else if (st == bike_flasher_e) { + if (bike_flasher_brightness > 2) + bike_flasher_brightness --; + set_level(bike_flasher_brightness); + } + #endif + + return EVENT_HANDLED; + } + // release hold: save new strobe settings + else if (event == EV_click2_hold_release) { + save_config(); + return EVENT_HANDLED; + } + #if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE) + // clock tick: bump the random seed + else if (event == EV_tick) { + // un-reverse after 1 second + if (arg == TICKS_PER_SECOND) ramp_direction = 1; + + pseudo_rand_seed += arg; + return EVENT_HANDLED; + } + #endif + return EVENT_NOT_HANDLED; +} + +#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) +inline void party_tactical_strobe_mode_iter(uint8_t st) { + // one iteration of main loop() + uint8_t del = strobe_delays[st]; + // TODO: make tac strobe brightness configurable? + set_level(STROBE_BRIGHTNESS); + if (0) {} // placeholde0 + #ifdef USE_PARTY_STROBE_MODE + else if (st == party_strobe_e) { // party strobe + if (del < 42) delay_zero(); + else nice_delay_ms(1); + } + #endif + #ifdef USE_TACTICAL_STROBE_MODE + else { //tactical strobe + nice_delay_ms(del >> 1); + } + #endif + set_level(0); + nice_delay_ms(del); // no return check necessary on final delay +} +#endif + +#ifdef USE_LIGHTNING_MODE +inline void lightning_storm_iter() { + // one iteration of main loop() + int16_t brightness; + uint16_t rand_time; + + // turn the emitter on at a random level, + // for a random amount of time between 1ms and 32ms + //rand_time = 1 << (pseudo_rand() % 7); + rand_time = pseudo_rand() & 63; + brightness = 1 << (pseudo_rand() % 7); // 1, 2, 4, 8, 16, 32, 64 + brightness += 1 << (pseudo_rand() % 5); // 2 to 80 now + brightness += pseudo_rand() % brightness; // 2 to 159 now (w/ low bias) + if (brightness > MAX_LEVEL) brightness = MAX_LEVEL; + set_level(brightness); + nice_delay_ms(rand_time); + + // decrease the brightness somewhat more gradually, like lightning + uint8_t stepdown = brightness >> 3; + if (stepdown < 1) stepdown = 1; + while(brightness > 1) { + nice_delay_ms(rand_time); + brightness -= stepdown; + if (brightness < 0) brightness = 0; + set_level(brightness); + /* + if ((brightness < MAX_LEVEL/2) && (! (pseudo_rand() & 15))) { + brightness <<= 1; + set_level(brightness); + } + */ + if (! (pseudo_rand() & 3)) { + nice_delay_ms(rand_time); + set_level(brightness>>1); + } + } + + // turn the emitter off, + // for a random amount of time between 1ms and 8192ms + // (with a low bias) + rand_time = 1 << (pseudo_rand() % 13); + rand_time += pseudo_rand() % rand_time; + set_level(0); + nice_delay_ms(rand_time); // no return check necessary on final delay +} +#endif + +#ifdef USE_BIKE_FLASHER_MODE +inline void bike_flasher_iter() { + // one iteration of main loop() + uint8_t burst = bike_flasher_brightness << 1; + if (burst > MAX_LEVEL) burst = MAX_LEVEL; + for(uint8_t i=0; i<4; i++) { + set_level(burst); + nice_delay_ms(5); + set_level(bike_flasher_brightness); + nice_delay_ms(65); + } + nice_delay_ms(720); // no return check necessary on final delay +} +#endif + +#endif // ifdef USE_STROBE_STATE + +#ifdef USE_CANDLE_MODE +uint8_t candle_mode_state(Event event, uint16_t arg) { + static int8_t ramp_direction = 1; + #define MAX_CANDLE_LEVEL (RAMP_LENGTH-CANDLE_AMPLITUDE-15) + static uint8_t candle_wave1 = 0; + static uint8_t candle_wave2 = 0; + static uint8_t candle_wave3 = 0; + static uint8_t candle_wave2_speed = 0; + // these should add up to 100 + #define CANDLE_WAVE1_MAXDEPTH 30 + #define CANDLE_WAVE2_MAXDEPTH 45 + #define CANDLE_WAVE3_MAXDEPTH 25 + static const uint8_t candle_wave1_depth = CANDLE_WAVE1_MAXDEPTH * CANDLE_AMPLITUDE / 100; + static uint8_t candle_wave2_depth = CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100; + static uint8_t candle_wave3_depth = CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100; + static uint8_t candle_mode_brightness = 24; + static uint8_t candle_mode_timer = 0; + #define TICKS_PER_CANDLE_MINUTE 4096 // about 65 seconds + #define MINUTES_PER_CANDLE_HALFHOUR 27 // ish + + if (event == EV_enter_state) { + candle_mode_timer = 0; // in case any time was left over from earlier + ramp_direction = 1; + return EVENT_HANDLED; + } + // 2 clicks: cancel timer + else if (event == EV_2clicks) { + // parent state just rotated through strobe/flasher modes, + // so cancel timer... in case any time was left over from earlier + candle_mode_timer = 0; + return EVENT_HANDLED; + } + // hold: change brightness (brighter) + else if (event == EV_click1_hold) { + // ramp away from extremes + if (! arg) { + if (candle_mode_brightness >= MAX_CANDLE_LEVEL) { ramp_direction = -1; } + else if (candle_mode_brightness <= 1) { ramp_direction = 1; } + } + // change brightness, but not too far + candle_mode_brightness += ramp_direction; + if (candle_mode_brightness < 1) candle_mode_brightness = 1; + else if (candle_mode_brightness > MAX_CANDLE_LEVEL) candle_mode_brightness = MAX_CANDLE_LEVEL; + return EVENT_HANDLED; + } + // reverse ramp direction on hold release + else if (event == EV_click1_hold_release) { + ramp_direction = -ramp_direction; + return EVENT_HANDLED; + } + // click, hold: change brightness (dimmer) + else if (event == EV_click2_hold) { + ramp_direction = 1; + if (candle_mode_brightness > 1) + candle_mode_brightness --; + return EVENT_HANDLED; + } + // 3 clicks: add 30m to candle timer + else if (event == EV_3clicks) { + if (candle_mode_timer < (255 - MINUTES_PER_CANDLE_HALFHOUR)) { + // add 30m to the timer + candle_mode_timer += MINUTES_PER_CANDLE_HALFHOUR; + // blink to confirm + set_level(actual_level + 32); + delay_4ms(2); + } + return EVENT_HANDLED; + } + // clock tick: animate candle brightness + else if (event == EV_tick) { + // un-reverse after 1 second + if (arg == TICKS_PER_SECOND) ramp_direction = 1; + + // self-timer dims the light during the final minute + uint8_t subtract = 0; + if (candle_mode_timer == 1) { + subtract = ((candle_mode_brightness+CANDLE_AMPLITUDE) + * ((arg & (TICKS_PER_CANDLE_MINUTE-1)) >> 4)) + >> 8; + } + // we passed a minute mark, decrease timer if it's running + if ((arg & (TICKS_PER_CANDLE_MINUTE-1)) == (TICKS_PER_CANDLE_MINUTE - 1)) { + if (candle_mode_timer > 0) { + candle_mode_timer --; + //set_level(0); delay_4ms(2); + // if the timer ran out, shut off + if (! candle_mode_timer) { + set_state(off_state, 0); + } + } + } + // 3-oscillator synth for a relatively organic pattern + uint8_t add; + add = ((triangle_wave(candle_wave1) * candle_wave1_depth) >> 8) + + ((triangle_wave(candle_wave2) * candle_wave2_depth) >> 8) + + ((triangle_wave(candle_wave3) * candle_wave3_depth) >> 8); + int8_t brightness = candle_mode_brightness + add - subtract; + if (brightness < 0) { brightness = 0; } + set_level(brightness); + + // wave1: slow random LFO + // TODO: make wave slower and more erratic? + if ((arg & 1) == 0) candle_wave1 += pseudo_rand() & 1; + // wave2: medium-speed erratic LFO + candle_wave2 += candle_wave2_speed; + // wave3: erratic fast wave + candle_wave3 += pseudo_rand() % 37; + // S&H on wave2 frequency to make it more erratic + if ((pseudo_rand() & 0b00111111) == 0) + candle_wave2_speed = pseudo_rand() % 13; + // downward sawtooth on wave2 depth to simulate stabilizing + if ((candle_wave2_depth > 0) && ((pseudo_rand() & 0b00111111) == 0)) + candle_wave2_depth --; + // random sawtooth retrigger + if (pseudo_rand() == 0) { + // random amplitude + //candle_wave2_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2)); + candle_wave2_depth = pseudo_rand() % (CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100); + //candle_wave3_depth = 5; + candle_wave2 = 0; + } + // downward sawtooth on wave3 depth to simulate stabilizing + if ((candle_wave3_depth > 2) && ((pseudo_rand() & 0b00011111) == 0)) + candle_wave3_depth --; + if ((pseudo_rand() & 0b01111111) == 0) + // random amplitude + //candle_wave3_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2)); + candle_wave3_depth = pseudo_rand() % (CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} +#endif // #ifdef USE_CANDLE_MODE + + +#ifdef USE_BORING_STROBE_STATE +uint8_t boring_strobe_state(Event event, uint16_t arg) { + // police strobe and SOS, meh + // 'st' reduces ROM size by avoiding access to a volatile var + // (maybe I should just make it nonvolatile?) + uint8_t st = boring_strobe_type; + + momentary_mode = 1; // 0 = ramping, 1 = strobes + + if (event == EV_enter_state) { + return EVENT_HANDLED; + } + // 1 click: off + else if (event == EV_1click) { + // reset to police strobe for next time + boring_strobe_type = 0; + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: rotate through strobe/flasher modes + else if (event == EV_2clicks) { + boring_strobe_type = (st + 1) % NUM_BORING_STROBES; + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + +#ifdef USE_POLICE_STROBE_MODE +inline void police_strobe_iter() { + // one iteration of main loop() + // flash at 16 Hz then 8 Hz, 8 times each + for (uint8_t del=41; del<100; del+=41) { + for (uint8_t f=0; f<8; f++) { + set_level(STROBE_BRIGHTNESS); + nice_delay_ms(del >> 1); + set_level(0); + nice_delay_ms(del); + } + } +} +#endif + +#ifdef USE_SOS_MODE +void sos_blink(uint8_t num, uint8_t dah) { + #define DIT_LENGTH 200 + for (; num > 0; num--) { + set_level(memorized_level); + nice_delay_ms(DIT_LENGTH); + if (dah) { // dah is 3X as long as a dit + nice_delay_ms(DIT_LENGTH*2); + } + set_level(0); + // one "off" dit between blinks + nice_delay_ms(DIT_LENGTH); + } + // three "off" dits (or one "dah") between letters + nice_delay_ms(DIT_LENGTH*2); +} + +inline void sos_mode_iter() { + // one iteration of main loop() + nice_delay_ms(1000); + sos_blink(3, 0); // S + sos_blink(3, 1); // O + sos_blink(3, 0); // S + nice_delay_ms(1000); +} +#endif // #ifdef USE_SOS_MODE +#endif // #ifdef USE_BORING_STROBE_STATE + + +#ifdef USE_BATTCHECK +uint8_t battcheck_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + #if defined(USE_GOODNIGHT_MODE) || defined(USE_BEACON_MODE) + // 2 clicks: next mode + else if (event == EV_2clicks) { + #ifdef USE_GOODNIGHT_MODE + set_state(goodnight_state, 0); + #elif defined(USE_BEACON_MODE) + set_state(beacon_state, 0); + #endif + return EVENT_HANDLED; + } + #endif + return EVENT_NOT_HANDLED; +} +#endif + + +#ifdef USE_THERMAL_REGULATION +uint8_t tempcheck_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + #if 0 // not part of a loop in this UI + // 2 clicks: battcheck mode + else if (event == EV_2clicks) { + set_state(battcheck_state, 0); + return EVENT_HANDLED; + } + #endif + // 4 clicks: thermal config mode + else if (event == EV_4clicks) { + push_state(thermal_config_state, 0); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} +#endif + + +#ifdef USE_BEACON_MODE +uint8_t beacon_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // TODO: use sleep ticks to measure time between pulses, + // to save power + // 2 clicks: tempcheck mode + else if (event == EV_2clicks) { + #ifdef USE_THERMAL_REGULATION + set_state(tempcheck_state, 0); + #else + set_state(battcheck_state, 0); + #endif + return EVENT_HANDLED; + } + // 4 clicks: beacon config mode + else if (event == EV_4clicks) { + push_state(beacon_config_state, 0); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} +#endif // #ifdef USE_BEACON_MODE + + +#ifdef USE_GOODNIGHT_MODE +#define GOODNIGHT_TICKS_PER_STEPDOWN (GOODNIGHT_TIME*TICKS_PER_SECOND*60L/GOODNIGHT_LEVEL) +uint8_t goodnight_state(Event event, uint16_t arg) { + static uint16_t ticks_since_stepdown = 0; + // blink on start + if (event == EV_enter_state) { + ticks_since_stepdown = 0; + blink_confirm(2); + set_level(GOODNIGHT_LEVEL); + return EVENT_HANDLED; + } + // 1 click: off + else if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: beacon mode + else if (event == EV_2clicks) { + #ifdef USE_BEACON_MODE + set_state(beacon_state, 0); + #elif defined(USE_TEMPCHECK_MODE) + set_state(tempcheck_state, 0); + #endif + return EVENT_HANDLED; + } + // tick: step down (maybe) or off (maybe) + else if (event == EV_tick) { + if (++ticks_since_stepdown > GOODNIGHT_TICKS_PER_STEPDOWN) { + ticks_since_stepdown = 0; + set_level(actual_level-1); + if (! actual_level) { + #if 0 // test blink, to help measure timing + set_level(MAX_LEVEL>>2); + delay_4ms(8/2); + set_level(0); + #endif + set_state(off_state, 0); + } + } + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} +#endif + + +uint8_t lockout_state(Event event, uint16_t arg) { + #ifdef MOON_DURING_LOCKOUT_MODE + // momentary(ish) moon mode during lockout + // button is being held + if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { + #ifdef LOCKOUT_MOON_LOWEST + // Use lowest moon configured + uint8_t lvl = ramp_smooth_floor; + if (ramp_discrete_floor < lvl) lvl = ramp_discrete_floor; + set_level(lvl); + #elif defined(LOCKOUT_MOON_FANCY) + uint8_t levels[] = { ramp_smooth_floor, ramp_discrete_floor }; + if ((event & 0x0f) == 2) { + set_level(levels[ramp_style^1]); + } else { + set_level(levels[ramp_style]); + } + #else + // Use moon from current ramp + set_level(nearest_level(1)); + #endif + } + // button was released + else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { + set_level(0); + } + #endif + + // regular event handling + // conserve power while locked out + // (allow staying awake long enough to exit, but otherwise + // be persistent about going back to sleep every few seconds + // even if the user keeps pressing the button) + #ifdef USE_INDICATOR_LED + if (event == EV_enter_state) { + indicator_led(indicator_led_mode >> 2); + } else + #endif + if (event == EV_tick) { + if (arg > TICKS_PER_SECOND*2) { + go_to_standby = 1; + #ifdef USE_INDICATOR_LED + indicator_led(indicator_led_mode >> 2); + #endif + } + return EVENT_HANDLED; + } + #if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED) + else if (event == EV_sleep_tick) { + if ((indicator_led_mode & 0b00001100) == 0b00001100) { + indicator_blink(arg); + } + return EVENT_HANDLED; + } + #endif + #ifdef USE_INDICATOR_LED + // 3 clicks: rotate through indicator LED modes (lockout mode) + else if (event == EV_3clicks) { + uint8_t mode = indicator_led_mode >> 2; + #ifdef TICK_DURING_STANDBY + mode = (mode + 1) & 3; + #else + mode = (mode + 1) % 3; + #endif + #ifdef INDICATOR_LED_SKIP_LOW + if (mode == 1) { mode ++; } + #endif + indicator_led_mode = (mode << 2) + (indicator_led_mode & 0x03); + indicator_led(mode); + save_config(); + return EVENT_HANDLED; + } + #if 0 // old method, deprecated in favor of "7 clicks from off" + // click, click, hold: rotate through indicator LED modes (off mode) + else if (event == EV_click3_hold) { + #ifndef USE_INDICATOR_LED_WHILE_RAMPING + // if main LED obscures aux LEDs, turn it off + set_level(0); + #endif + #ifdef TICK_DURING_STANDBY + uint8_t mode = (arg >> 5) & 3; + #else + uint8_t mode = (arg >> 5) % 3; + #endif + #ifdef INDICATOR_LED_SKIP_LOW + if (mode == 1) { mode ++; } + #endif + indicator_led_mode = (indicator_led_mode & 0b11111100) | mode; + #ifdef TICK_DURING_STANDBY + if (mode == 3) + indicator_led(mode & (arg&3)); + else + indicator_led(mode); + #else + indicator_led(mode); + #endif + //save_config(); + return EVENT_HANDLED; + } + // click, click, hold, release: save indicator LED mode (off mode) + else if (event == EV_click3_hold_release) { + save_config(); + return EVENT_HANDLED; + } + #endif + #endif + // 4 clicks: exit + else if (event == EV_4clicks) { + blink_confirm(1); + set_state(off_state, 0); + return EVENT_HANDLED; + } + + return EVENT_NOT_HANDLED; +} + + +uint8_t momentary_state(Event event, uint16_t arg) { + // TODO: momentary strobe here? (for light painting) + + // init strobe mode, if relevant + if ((event == EV_enter_state) && (momentary_mode == 1)) { + strobe_state(event, arg); + } + + // light up when the button is pressed; go dark otherwise + // button is being held + if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { + momentary_active = 1; + // 0 = ramping, 1 = strobes + if (momentary_mode == 0) { + set_level(memorized_level); + } + return EVENT_HANDLED; + } + // button was released + else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { + momentary_active = 0; + set_level(0); + //go_to_standby = 1; // sleep while light is off + return EVENT_HANDLED; + } + + // Sleep, dammit! (but wait a few seconds first) + // (because standby mode uses such little power that it can interfere + // with exiting via tailcap loosen+tighten unless you leave power + // disconnected for several seconds, so we want to be awake when that + // happens to speed up the process) + else if (event == EV_tick) { + if (momentary_active) { + // 0 = ramping, 1 = strobes + if (momentary_mode == 1) { + return strobe_state(event, arg); + } + } + else { + if (arg > TICKS_PER_SECOND*15) { // sleep after 15 seconds + go_to_standby = 1; // sleep while light is off + // TODO: lighted button should use lockout config? + } + } + return EVENT_HANDLED; + } + + return EVENT_NOT_HANDLED; +} + + +#ifdef USE_MUGGLE_MODE +uint8_t muggle_state(Event event, uint16_t arg) { + static int8_t ramp_direction; + static int8_t muggle_off_mode; + + // turn LED off when we first enter the mode + if (event == EV_enter_state) { + ramp_direction = 1; + + #ifdef START_AT_MEMORIZED_LEVEL + memorized_level = arg; + muggle_off_mode = 0; + set_level(memorized_level); + + if (! muggle_mode_active) { // don't write eeprom at every boot + muggle_mode_active = 1; + save_config(); + } + #else + muggle_mode_active = 1; + save_config(); + + muggle_off_mode = 1; + //memorized_level = MAX_1x7135; + memorized_level = (MUGGLE_FLOOR + MUGGLE_CEILING) / 2; + #endif + return EVENT_HANDLED; + } + // initial press: moon hint + else if (event == EV_click1_press) { + if (muggle_off_mode) + set_level(MUGGLE_FLOOR); + } + // initial release: direct to memorized level + else if (event == EV_click1_release) { + if (muggle_off_mode) + set_level(memorized_level); + } + // if the user keeps pressing, turn off + else if (event == EV_click2_press) { + muggle_off_mode = 1; + set_level(0); + } + // 1 click: on/off + else if (event == EV_1click) { + muggle_off_mode ^= 1; + if (muggle_off_mode) { + set_level(0); + } + /* + else { + set_level(memorized_level); + } + */ + return EVENT_HANDLED; + } + // hold: change brightness + else if (event == EV_click1_hold) { + // ramp at half speed + if (arg & 1) return EVENT_HANDLED; + + // if off, start at bottom + if (muggle_off_mode) { + muggle_off_mode = 0; + ramp_direction = 1; + set_level(MUGGLE_FLOOR); + } + else { + uint8_t m; + m = actual_level; + // ramp down if already at ceiling + if ((arg <= 1) && (m >= MUGGLE_CEILING)) ramp_direction = -1; + // ramp + m += ramp_direction; + if (m < MUGGLE_FLOOR) + m = MUGGLE_FLOOR; + if (m > MUGGLE_CEILING) + m = MUGGLE_CEILING; + memorized_level = m; + set_level(m); + } + return EVENT_HANDLED; + } + // reverse ramp direction on hold release + else if (event == EV_click1_hold_release) { + ramp_direction = -ramp_direction; + #ifdef START_AT_MEMORIZED_LEVEL + save_config_wl(); // momentary use should retain brightness level + #endif + return EVENT_HANDLED; + } + /* + // click, hold: change brightness (dimmer) + else if (event == EV_click2_hold) { + ramp_direction = 1; + if (memorized_level > MUGGLE_FLOOR) + memorized_level = actual_level - 1; + set_level(memorized_level); + return EVENT_HANDLED; + } + */ + // 6 clicks: exit muggle mode + else if (event == EV_6clicks) { + blink_confirm(1); + muggle_mode_active = 0; + save_config(); + set_state(off_state, 0); + return EVENT_HANDLED; + } + // tick: housekeeping + else if (event == EV_tick) { + // un-reverse after 1 second + if (arg == TICKS_PER_SECOND) ramp_direction = 1; + + // turn off, but don't go to the main "off" state + if (muggle_off_mode) { + if (arg > TICKS_PER_SECOND*1) { // sleep after 1 second + go_to_standby = 1; // sleep while light is off + } + } + return EVENT_HANDLED; + } + #ifdef USE_THERMAL_REGULATION + // overheating is handled specially in muggle mode + else if(event == EV_temperature_high) { + #if 0 + blip(); + #endif + // step down proportional to the amount of overheating + uint8_t new = actual_level - arg; + if (new < MUGGLE_FLOOR) { new = MUGGLE_FLOOR; } + set_level(new); + return EVENT_HANDLED; + } + #endif + // low voltage is handled specially in muggle mode + else if(event == EV_voltage_low) { + uint8_t lvl = (actual_level >> 1) + (actual_level >> 2); + if (lvl >= MUGGLE_FLOOR) { + set_level(lvl); + } else { + muggle_off_mode = 1; + } + return EVENT_HANDLED; + } + + return EVENT_NOT_HANDLED; +} +#endif + + +// ask the user for a sequence of numbers, then save them and return to caller +uint8_t config_state_base(Event event, uint16_t arg, + uint8_t num_config_steps, + void (*savefunc)()) { + static uint8_t config_step; + if (event == EV_enter_state) { + config_step = 0; + set_level(0); + return EVENT_HANDLED; + } + // advance forward through config steps + else if (event == EV_tick) { + if (config_step < num_config_steps) { + push_state(number_entry_state, config_step + 1); + } + else { + // TODO: blink out some sort of success pattern + savefunc(); + save_config(); + //set_state(retstate, retval); + pop_state(); + } + return EVENT_HANDLED; + } + // an option was set (return from number_entry_state) + else if (event == EV_reenter_state) { + config_state_values[config_step] = number_entry_value; + config_step ++; + return EVENT_HANDLED; + } + //return EVENT_NOT_HANDLED; + // eat all other events; don't pass any through to parent + return EVENT_HANDLED; +} + +#ifdef USE_RAMP_CONFIG +void ramp_config_save() { + // parse values + uint8_t val; + if (ramp_style) { // discrete / stepped ramp + + val = config_state_values[0]; + if (val) { ramp_discrete_floor = val; } + + val = config_state_values[1]; + if (val) { ramp_discrete_ceil = MAX_LEVEL + 1 - val; } + + val = config_state_values[2]; + if (val) ramp_discrete_steps = val; + + } else { // smooth ramp + + val = config_state_values[0]; + if (val) { ramp_smooth_floor = val; } + + val = config_state_values[1]; + if (val) { ramp_smooth_ceil = MAX_LEVEL + 1 - val; } + + } +} + +uint8_t ramp_config_state(Event event, uint16_t arg) { + uint8_t num_config_steps; + num_config_steps = 2 + ramp_style; + return config_state_base(event, arg, + num_config_steps, ramp_config_save); +} +#endif // #ifdef USE_RAMP_CONFIG + + +#ifdef USE_THERMAL_REGULATION +void thermal_config_save() { + // parse values + uint8_t val; + + // calibrate room temperature + val = config_state_values[0]; + if (val) { + int8_t rawtemp = temperature - therm_cal_offset; + therm_cal_offset = val - rawtemp; + reset_thermal_history = 1; // invalidate all recent temperature data + } + + val = config_state_values[1]; + if (val) { + // set maximum heat limit + therm_ceil = 30 + val - 1; + } + if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL; +} + +uint8_t thermal_config_state(Event event, uint16_t arg) { + return config_state_base(event, arg, + 2, thermal_config_save); +} +#endif // #ifdef USE_THERMAL_REGULATION + + +#ifdef USE_BEACON_MODE +void beacon_config_save() { + // parse values + uint8_t val = config_state_values[0]; + if (val) { + beacon_seconds = val; + } +} + +uint8_t beacon_config_state(Event event, uint16_t arg) { + return config_state_base(event, arg, + 1, beacon_config_save); +} + +inline void beacon_mode_iter() { + // one iteration of main loop() + set_level(memorized_level); + nice_delay_ms(100); + set_level(0); + nice_delay_ms(((beacon_seconds) * 1000) - 100); +} +#endif // #ifdef USE_BEACON_MODE + + +uint8_t number_entry_state(Event event, uint16_t arg) { + static uint8_t value; + static uint8_t blinks_left; + static uint8_t entry_step; + static uint16_t wait_ticks; + if (event == EV_enter_state) { + value = 0; + blinks_left = arg; + entry_step = 0; + wait_ticks = 0; + return EVENT_HANDLED; + } + // advance through the process: + // 0: wait a moment + // 1: blink out the 'arg' value + // 2: wait a moment + // 3: "buzz" while counting clicks + // 4: save and exit + else if (event == EV_tick) { + // wait a moment + if ((entry_step == 0) || (entry_step == 2)) { + if (wait_ticks < TICKS_PER_SECOND/2) + wait_ticks ++; + else { + entry_step ++; + wait_ticks = 0; + } + } + // blink out the option number + else if (entry_step == 1) { + if (blinks_left) { + if ((wait_ticks & 31) == 10) { + set_level(RAMP_SIZE/4); + } + else if ((wait_ticks & 31) == 20) { + set_level(0); + } + else if ((wait_ticks & 31) == 31) { + blinks_left --; + } + wait_ticks ++; + } + else { + entry_step ++; + wait_ticks = 0; + } + } + else if (entry_step == 3) { // buzz while waiting for a number to be entered + wait_ticks ++; + // buzz for N seconds after last event + if ((wait_ticks & 3) == 0) { + set_level(RAMP_SIZE/6); + } + else if ((wait_ticks & 3) == 2) { + set_level(RAMP_SIZE/8); + } + // time out after 3 seconds + if (wait_ticks > TICKS_PER_SECOND*3) { + //number_entry_value = value; + set_level(0); + entry_step ++; + } + } + else if (entry_step == 4) { + number_entry_value = value; + pop_state(); + } + return EVENT_HANDLED; + } + // count clicks + else if (event == EV_click1_release) { + empty_event_sequence(); + if (entry_step == 3) { // only count during the "buzz" + value ++; + wait_ticks = 0; + // flash briefly + set_level(RAMP_SIZE/2); + delay_4ms(8/2); + set_level(0); + } + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + + +// find the ramp level closest to the target, +// using only the levels which are allowed in the current state +uint8_t nearest_level(int16_t target) { + // bounds check + // using int16_t here saves us a bunch of logic elsewhere, + // by allowing us to correct for numbers < 0 or > 255 in one central place + uint8_t mode_min = ramp_smooth_floor; + uint8_t mode_max = ramp_smooth_ceil; + if (ramp_style) { + mode_min = ramp_discrete_floor; + mode_max = ramp_discrete_ceil; + } + if (target < mode_min) return mode_min; + if (target > mode_max) return mode_max; + // the rest isn't relevant for smooth ramping + if (! ramp_style) return target; + + uint8_t ramp_range = ramp_discrete_ceil - ramp_discrete_floor; + ramp_discrete_step_size = ramp_range / (ramp_discrete_steps-1); + uint8_t this_level = ramp_discrete_floor; + + for(uint8_t i=0; i>1)) + return this_level; + } + return this_level; +} + + +void blink_confirm(uint8_t num) { + for (; num>0; num--) { + set_level(MAX_LEVEL/4); + delay_4ms(10/4); + set_level(0); + delay_4ms(100/4); + } +} + +// Just go dark for a moment to indicate to user that something happened +void blip() { + uint8_t temp = actual_level; + set_level(0); + delay_4ms(3); + set_level(temp); +} + + +#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY) +// beacon-like mode for the indicator LED +void indicator_blink(uint8_t arg) { + #define USE_FANCIER_BLINKING_INDICATOR + #ifdef USE_FANCIER_BLINKING_INDICATOR + + // fancy blink, set off/low/high levels here: + uint8_t seq[] = {0, 1, 2, 1, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0}; + indicator_led(seq[arg & 15]); + + #else // basic blink, 1/8th duty cycle + + if (! (arg & 7)) { + indicator_led(2); + } + else { + indicator_led(0); + } + + #endif +} +#endif + + +void load_config() { + if (load_eeprom()) { + ramp_style = eeprom[ramp_style_e]; + #ifdef USE_RAMP_CONFIG + ramp_smooth_floor = eeprom[ramp_smooth_floor_e]; + ramp_smooth_ceil = eeprom[ramp_smooth_ceil_e]; + ramp_discrete_floor = eeprom[ramp_discrete_floor_e]; + ramp_discrete_ceil = eeprom[ramp_discrete_ceil_e]; + ramp_discrete_steps = eeprom[ramp_discrete_steps_e]; + #endif + #ifdef USE_TINT_RAMPING + tint = eeprom[tint_e]; + #endif + #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) + strobe_type = eeprom[strobe_type_e]; // TODO: move this to eeprom_wl? + strobe_delays[0] = eeprom[strobe_delays_0_e]; + strobe_delays[1] = eeprom[strobe_delays_1_e]; + #endif + #ifdef USE_BIKE_FLASHER_MODE + bike_flasher_brightness = eeprom[bike_flasher_brightness_e]; + #endif + #ifdef USE_BEACON_MODE + beacon_seconds = eeprom[beacon_seconds_e]; + #endif + #ifdef USE_MUGGLE_MODE + muggle_mode_active = eeprom[muggle_mode_active_e]; + #endif + #ifdef USE_THERMAL_REGULATION + therm_ceil = eeprom[therm_ceil_e]; + therm_cal_offset = eeprom[therm_cal_offset_e]; + #endif + #ifdef USE_INDICATOR_LED + indicator_led_mode = eeprom[indicator_led_mode_e]; + #endif + } + #ifdef START_AT_MEMORIZED_LEVEL + if (load_eeprom_wl()) { + memorized_level = eeprom_wl[0]; + } + #endif +} + +void save_config() { + eeprom[ramp_style_e] = ramp_style; + #ifdef USE_RAMP_CONFIG + eeprom[ramp_smooth_floor_e] = ramp_smooth_floor; + eeprom[ramp_smooth_ceil_e] = ramp_smooth_ceil; + eeprom[ramp_discrete_floor_e] = ramp_discrete_floor; + eeprom[ramp_discrete_ceil_e] = ramp_discrete_ceil; + eeprom[ramp_discrete_steps_e] = ramp_discrete_steps; + #endif + #ifdef USE_TINT_RAMPING + eeprom[tint_e] = tint; + #endif + #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) + eeprom[strobe_type_e] = strobe_type; // TODO: move this to eeprom_wl? + eeprom[strobe_delays_0_e] = strobe_delays[0]; + eeprom[strobe_delays_1_e] = strobe_delays[1]; + #endif + #ifdef USE_BIKE_FLASHER_MODE + eeprom[bike_flasher_brightness_e] = bike_flasher_brightness; + #endif + #ifdef USE_BEACON_MODE + eeprom[beacon_seconds_e] = beacon_seconds; + #endif + #ifdef USE_MUGGLE_MODE + eeprom[muggle_mode_active_e] = muggle_mode_active; + #endif + #ifdef USE_THERMAL_REGULATION + eeprom[therm_ceil_e] = therm_ceil; + eeprom[therm_cal_offset_e] = therm_cal_offset; + #endif + #ifdef USE_INDICATOR_LED + eeprom[indicator_led_mode_e] = indicator_led_mode; + #endif + + save_eeprom(); +} + +#ifdef START_AT_MEMORIZED_LEVEL +void save_config_wl() { + eeprom_wl[0] = memorized_level; + save_eeprom_wl(); +} +#endif + + +void low_voltage() { + StatePtr state = current_state; + + // TODO: turn off aux LED(s) when power is really low + + if (0) {} // placeholder + + #ifdef USE_STROBE_STATE + // "step down" from strobe to something low + else if (state == strobe_state) { + set_state(steady_state, RAMP_SIZE/6); + } + #endif + + // in normal or muggle mode, step down or turn off + //else if ((state == steady_state) || (state == muggle_state)) { + else if (state == steady_state) { + if (actual_level > 1) { + uint8_t lvl = (actual_level >> 1) + (actual_level >> 2); + set_level(lvl); + #ifdef USE_THERMAL_REGULATION + target_level = lvl; + #ifdef USE_SET_LEVEL_GRADUALLY + // not needed? + //set_level_gradually(lvl); + #endif + #endif + } + else { + set_state(off_state, 0); + } + } + // all other modes, just turn off when voltage is low + else { + set_state(off_state, 0); + } +} + + +void setup() { + #ifdef START_AT_MEMORIZED_LEVEL + // dual switch: e-switch + power clicky + // power clicky acts as a momentary mode + load_config(); + + #ifdef USE_MUGGLE_MODE + if (muggle_mode_active) + push_state(muggle_state, memorized_level); + else + #endif + if (button_is_pressed()) + // hold button to go to moon + push_state(steady_state, 1); + else + // otherwise use memory + push_state(steady_state, memorized_level); + + #else // if not START_AT_MEMORIZED_LEVEL + + // blink at power-on to let user know power is connected + set_level(RAMP_SIZE/8); + delay_4ms(3); + set_level(0); + + load_config(); + + #ifdef USE_TINT_RAMPING + // add tint ramping underneath every other state + push_state(tint_ramping_state, 0); + #endif // ifdef USE_TINT_RAMPING + + #ifdef USE_MUGGLE_MODE + if (muggle_mode_active) + push_state(muggle_state, (MUGGLE_FLOOR+MUGGLE_CEILING)/2); + else + #endif + push_state(off_state, 0); + + #endif // ifdef START_AT_MEMORIZED_LEVEL +} + + +void loop() { + + StatePtr state = current_state; + + if (0) {} + + #ifdef USE_STROBE_STATE + else if ((state == strobe_state) + || ((state == momentary_state) && (momentary_mode == 1) && (momentary_active)) ) { // also handle momentary strobes + uint8_t st = strobe_type; + + switch(st) { + #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE) + #ifdef USE_PARTY_STROBE_MODE + case party_strobe_e: + #endif + #ifdef USE_TACTICAL_STROBE_MODE + case tactical_strobe_e: + #endif + party_tactical_strobe_mode_iter(st); + break; + #endif + + #ifdef USE_LIGHTNING_MODE + case lightning_storm_e: + lightning_storm_iter(); + break; + #endif + + #ifdef USE_BIKE_FLASHER_MODE + case bike_flasher_e: + bike_flasher_iter(); + break; + #endif + } + + } + #endif // #ifdef USE_STROBE_STATE + + #ifdef USE_BORING_STROBE_STATE + else if ((state == boring_strobe_state) + || ((state == momentary_state) && (momentary_mode == 1) && (momentary_active)) ) { // also handle momentary strobes + switch(boring_strobe_type) { + #ifdef USE_POLICE_STROBE_MODE + case 0: // police strobe + police_strobe_iter(); + break; + #endif + + #ifdef USE_SOS_MODE + default: // SOS + sos_mode_iter(); + break; + #endif + } + } + #endif // #ifdef USE_BORING_STROBE_STATE + + #ifdef USE_BATTCHECK + else if (state == battcheck_state) { + battcheck(); + } + #endif + + #ifdef USE_BEACON_MODE + else if (state == beacon_state) { + beacon_mode_iter(); + } + #endif + + #ifdef USE_THERMAL_REGULATION + // TODO: blink out therm_ceil during thermal_config_state? + else if (state == tempcheck_state) { + blink_num(temperature); + nice_delay_ms(1000); + } + #endif + + #ifdef USE_IDLE_MODE + else { + // doze until next clock tick + idle_mode(); + } + #endif + +} diff --git a/ui/meteor/meteor.c b/ui/meteor/meteor.c new file mode 100644 index 0000000..9c1c000 --- /dev/null +++ b/ui/meteor/meteor.c @@ -0,0 +1,556 @@ +/* + * Meteor: Meteor M43 clone UI for SpaghettiMonster. + * (in progress, not really in a usable state yet) + * + * Copyright (C) 2017 Selene ToyKeeper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "hwdef-Emisar_D4.h" +#define USE_LVP +#define USE_THERMAL_REGULATION +#define DEFAULT_THERM_CEIL 45 +#define USE_RAMPING +#define RAMP_LENGTH 150 +#define USE_BATTCHECK +#define BATTCHECK_6bars +#define DONT_DELAY_AFTER_BATTCHECK +//#define USE_EEPROM +//#define EEPROM_BYTES 5 +#include "spaghetti-monster.h" + +// FSM states +uint8_t base_off_state(Event event, uint16_t arg); +uint8_t ui1_off_state(Event event, uint16_t arg); +uint8_t ui2_off_state(Event event, uint16_t arg); +uint8_t ui3_off_state(Event event, uint16_t arg); +uint8_t base_on_state(Event event, uint16_t arg, uint8_t *mode, uint8_t *group); +uint8_t ui1_on_state(Event event, uint16_t arg); +uint8_t ui2_on_state(Event event, uint16_t arg); +uint8_t ui3_on_state(Event event, uint16_t arg); +uint8_t beacon_state(Event event, uint16_t arg); +uint8_t battcheck_state(Event event, uint16_t arg); +uint8_t strobe_state(Event event, uint16_t arg); +uint8_t biking_state(Event event, uint16_t arg); +uint8_t lockout_state(Event event, uint16_t arg); +uint8_t momentary_state(Event event, uint16_t arg); +// Not a FSM state, just handles stuff common to all low/med/hi states +uint8_t any_mode_state(Event event, uint16_t arg, uint8_t *primary, uint8_t *secondary, uint8_t *modes); + +#ifdef USE_EEPROM +void load_config(); +void save_config(); +#endif + +// fixed output levels +uint8_t levels[] = {3, 16, 30, 43, 56, 70, 83, 96, 110, 123, 137, MAX_LEVEL}; +// select an interface +uint8_t UI = 1; // 1, 2, or 3 +// UI1 +uint8_t UI1_mode = 0; +uint8_t UI1_mode1 = 1; +uint8_t UI1_mode2 = 1; +uint8_t UI1_group1[] = {0, 2}; +uint8_t UI1_group2[] = {6, 9}; +// UI2 +uint8_t UI2_mode = 0; +uint8_t UI2_mode1 = 1; +uint8_t UI2_mode2 = 0; +uint8_t UI2_mode3 = 0; +uint8_t UI2_mode4 = 0; // doesn't matter, makes other code easier +uint8_t UI2_group1[] = { 0, 2}; // moon, low +uint8_t UI2_group2[] = { 4, 6}; // mid1, mid2 +uint8_t UI2_group3[] = { 8, 10}; // high1, high2 +uint8_t UI2_group4[] = {11, 11}; // turbo only +// UI3 can access all levels, with 3 different mode memory slots +uint8_t UI3_mode = 0; +uint8_t UI3_mode1 = 2; +uint8_t UI3_mode2 = 5; +uint8_t UI3_mode3 = 8; + +#ifdef USE_THERMAL_REGULATION +// brightness before thermal step-down +uint8_t target_level = 0; +#endif + +void set_any_mode(uint8_t mode, uint8_t *group) { + set_level(levels[group[mode]]); + #ifdef USE_THERMAL_REGULATION + target_level = actual_level; + #endif +} + +void blink_fast() { + set_level(MAX_LEVEL/2); + delay_4ms(8/4); + set_level(0); +} + +uint8_t base_off_state(Event event, uint16_t arg) { + // turn emitter off when entering state + if (event == EV_enter_state) { + set_level(0); + // sleep while off (lower power use) + go_to_standby = 1; + // ensure we're in a real off state, not the base + switch(UI) { + case 1: set_state(ui1_off_state, 0); break; + case 2: set_state(ui2_off_state, 0); break; + default: set_state(ui3_off_state, 0); break; + } + return EVENT_HANDLED; + } + // 3 clicks: strobe mode + else if (event == EV_3clicks) { + set_state(beacon_state, 0); + return EVENT_HANDLED; + } + // 4 clicks: battcheck mode + else if (event == EV_4clicks) { + set_state(battcheck_state, 0); + return EVENT_HANDLED; + } + // 5 clicks: battcheck mode + else if (event == EV_5clicks) { + set_state(biking_state, 0); + return EVENT_HANDLED; + } + // 6 clicks: soft lockout mode + else if (event == EV_6clicks) { + set_state(lockout_state, 0); + return EVENT_HANDLED; + } + // 9 clicks: activate UI1 + else if (event == EV_9clicks) { + blink_fast(); + set_state(ui1_off_state, 0); + return EVENT_HANDLED; + } + // 10 clicks: activate UI2 + else if (event == EV_10clicks) { + blink_fast(); + set_state(ui2_off_state, 0); + return EVENT_HANDLED; + } + // 11 clicks: activate UI3 + else if (event == EV_11clicks) { + blink_fast(); + set_state(ui3_off_state, 0); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + +uint8_t ui1_off_state(Event event, uint16_t arg) { + UI = 1; + if (event == EV_enter_state) { + return EVENT_HANDLED; + } + // 1 click: low modes + if (event == EV_1click) { + set_any_mode(UI1_mode1, UI1_group1); + set_state(ui1_on_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: high modes + else if (event == EV_2clicks) { + set_any_mode(UI1_mode2, UI1_group2); + set_state(ui1_on_state, 1); + return EVENT_HANDLED; + } + // hold: turbo + else if (event == EV_hold) { + if (arg == 0) { + set_level(MAX_LEVEL); + } + //set_state(ui1_on_state, 3); + return EVENT_HANDLED; + } + // release hold: off + else if (event == EV_click1_hold_release) { + set_state(base_off_state, 0); + return EVENT_HANDLED; + } + return base_off_state(event, arg); +} + +uint8_t ui2_off_state(Event event, uint16_t arg) { + UI = 2; + if (event == EV_enter_state) { + return EVENT_HANDLED; + } + // 1 click: low modes + if (event == EV_1click) { + set_any_mode(UI2_mode1, UI2_group1); + set_state(ui2_on_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: high modes + else if (event == EV_2clicks) { + set_any_mode(UI2_mode3, UI2_group3); + set_state(ui2_on_state, 2); + return EVENT_HANDLED; + } + // hold: turbo + else if (event == EV_hold) { + if (arg == 0) { + set_level(MAX_LEVEL); + } + //set_state(ui1_on_state, 3); + return EVENT_HANDLED; + } + // release hold: off + else if (event == EV_click1_hold_release) { + set_state(base_off_state, 0); + return EVENT_HANDLED; + } + return base_off_state(event, arg); +} + +uint8_t ui3_off_state(Event event, uint16_t arg) { + UI = 3; + if (event == EV_enter_state) { + return EVENT_HANDLED; + } + // 1 click: memory slot 1 + if (event == EV_1click) { + set_level(levels[UI3_mode1]); + set_state(ui3_on_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: memory slot 2 + else if (event == EV_2clicks) { + set_level(levels[UI3_mode2]); + set_state(ui3_on_state, 1); + return EVENT_HANDLED; + } + // Click, hold: memory slot 3 + else if (event == EV_click2_hold) { + set_level(levels[UI3_mode3]); + set_state(ui3_on_state, 2); + return EVENT_HANDLED; + } + // hold: turbo + else if (event == EV_hold) { + if (arg == 0) { + set_level(MAX_LEVEL); + } + //set_state(ui1_on_state, 3); + return EVENT_HANDLED; + } + // release hold: off + else if (event == EV_click1_hold_release) { + set_state(base_off_state, 0); + return EVENT_HANDLED; + } + return base_off_state(event, arg); +} + +uint8_t base_on_state(Event event, uint16_t arg, uint8_t *mode, uint8_t *group) { + // 1 click: off + if (event == EV_1click) { + set_state(base_off_state, 0); + return EVENT_HANDLED; + } + #ifdef USE_THERMAL_REGULATION + // overheating: drop by an amount proportional to how far we are above the ceiling + else if (event == EV_temperature_high) { + if (actual_level > MAX_LEVEL/4) { + uint8_t stepdown = actual_level - arg; + if (stepdown < MAX_LEVEL/4) stepdown = MAX_LEVEL/4; + set_level(stepdown); + } + return EVENT_HANDLED; + } + // underheating: increase slowly if we're lower than the target + // (proportional to how low we are) + else if (event == EV_temperature_low) { + if (actual_level < target_level) { + uint8_t stepup = actual_level + (arg>>1); + if (stepup > target_level) stepup = target_level; + set_level(stepup); + } + return EVENT_HANDLED; + } + #endif + return EVENT_NOT_HANDLED; +} + +uint8_t ui1_on_state(Event event, uint16_t arg) { + // turn on LED when entering the mode + static uint8_t *mode = &UI1_mode1; + static uint8_t *group = UI1_group1; + if (event == EV_enter_state) { + UI1_mode = arg; + } + if (UI1_mode == 0) { + mode = &UI1_mode1; + group = UI1_group1; + } + else { + mode = &UI1_mode2; + group = UI1_group2; + } + + if (event == EV_enter_state) { + set_any_mode(*mode, group); + return EVENT_HANDLED; + } + // 2 clicks: toggle moon/low or mid/high + else if (event == EV_2clicks) { + *mode ^= 1; + set_any_mode(*mode, group); + return EVENT_HANDLED; + } + // hold: turbo + else if (event == EV_hold) { + if (arg == 0) set_level(MAX_LEVEL); + return EVENT_HANDLED; + } + // release: exit turbo + else if (event == EV_click1_hold_release) { + set_any_mode(*mode, group); + return EVENT_HANDLED; + } + return base_on_state(event, arg, mode, group); +} + +uint8_t ui2_on_state(Event event, uint16_t arg) { + // turn on LED when entering the mode + static uint8_t *mode = &UI2_mode1; + static uint8_t *group = UI2_group1; + if (event == EV_enter_state) { + UI2_mode = arg; + } + switch (UI2_mode) { + case 0: + mode = &UI2_mode1; + group = UI2_group1; + break; + case 1: + mode = &UI2_mode2; + group = UI2_group2; + break; + case 2: + mode = &UI2_mode3; + group = UI2_group3; + break; + default: // turbo only + mode = &UI2_mode4; + group = UI2_group4; + break; + } + + if (event == EV_enter_state) { + set_any_mode(*mode, group); + return EVENT_HANDLED; + } + // 2 clicks: toggle moon/low, mid1/mid2, or high1/high2 + else if (event == EV_2clicks) { + *mode ^= 1; + set_any_mode(*mode, group); + return EVENT_HANDLED; + } + // hold: rotate through low/mid/high/turbo + else if (event == EV_hold) { + if (arg % HOLD_TIMEOUT == 0) { + UI2_mode = (UI2_mode + 1) & 3; + } + else if (arg % HOLD_TIMEOUT == 1) { + set_any_mode(*mode, group); + } + return EVENT_HANDLED; + } + return base_on_state(event, arg, mode, group); +} + +uint8_t ui3_on_state(Event event, uint16_t arg) { + // turn on LED when entering the mode + static uint8_t *mode = &UI3_mode1; + if (event == EV_enter_state) { + UI3_mode = arg; + } + // 2 clicks: rotate through mode1/mode2/mode3 + else if (event == EV_2clicks) { + UI3_mode = (UI3_mode + 1) % 3; + } + // short click, long click: rotate through mode3/mode2/mode1 + /* + else if (event == EV_click1_hold) { + if (arg % HOLD_TIMEOUT == 0) + UI3_mode = (UI3_mode + 4) % 3; + } + */ + switch (UI3_mode) { + case 0: + mode = &UI3_mode1; + break; + case 1: + mode = &UI3_mode2; + break; + default: + mode = &UI3_mode3; + break; + } + + if ((event == EV_enter_state) || (event == EV_2clicks)) { + set_level(levels[*mode]); + return EVENT_HANDLED; + } + // short click, long click: rotate through mode3/mode2/mode1 + /* + else if (event == EV_click1_hold) { + set_level(levels[*mode]); + return EVENT_HANDLED; + } + */ + // hold: turbo + // Click, hold: ramp up + // release hold, hold again: ramp in opposite direction + return base_on_state(event, arg, mode, levels); +} + + +uint8_t blinky_base_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(base_off_state, 0); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + +uint8_t beacon_state(Event event, uint16_t arg) { + return blinky_base_state(event, arg); +} + +uint8_t battcheck_state(Event event, uint16_t arg) { + return EVENT_NOT_HANDLED; +} + +uint8_t strobe_state(Event event, uint16_t arg) { + return blinky_base_state(event, arg); +} + +uint8_t biking_state(Event event, uint16_t arg) { + return blinky_base_state(event, arg); +} + +uint8_t lockout_state(Event event, uint16_t arg) { + return blinky_base_state(event, arg); +} + +uint8_t momentary_state(Event event, uint16_t arg) { + return blinky_base_state(event, arg); +} + + +void low_voltage() { + if ((current_state == ui1_on_state) || + (current_state == ui2_on_state) || + (current_state == ui3_on_state)) { + if (actual_level > 5) { + set_level(actual_level >> 1); + } + else { + set_state(base_off_state, 0); + } + } + /* + // "step down" from blinkies to low + else if (current_state == strobe_beacon_state) { + set_state(low_mode_state, 0); + } + */ +} + +void strobe(uint8_t level, uint16_t ontime, uint16_t offtime) { + set_level(level); + if (! nice_delay_ms(ontime)) return; + set_level(0); + nice_delay_ms(offtime); +} + +#ifdef USE_EEPROM +void load_config() { + if (load_eeprom()) { + H1 = !(!(eeprom[0] & 0b00000100)); + M1 = !(!(eeprom[0] & 0b00000010)); + L1 = !(!(eeprom[0] & 0b00000001)); + H2 = eeprom[1]; + M2 = eeprom[2]; + L2 = eeprom[3]; + strobe_beacon_mode = eeprom[4]; + } +} + +void save_config() { + eeprom[0] = (H1<<2) | (M1<<1) | (L1); + eeprom[1] = H2; + eeprom[2] = M2; + eeprom[3] = L2; + eeprom[4] = strobe_beacon_mode; + + save_eeprom(); +} +#endif + +void setup() { + set_level(RAMP_SIZE/8); + delay_4ms(3); + set_level(0); + + #ifdef USE_EEPROM + load_config(); + #endif + + push_state(base_off_state, 0); +} + +void loop() { + if (0) {} + /* + if (current_state == strobe_beacon_state) { + switch(strobe_beacon_mode) { + // 0.2 Hz beacon at L1 + case 0: + strobe(low_modes[0], 500, 4500); + break; + // 0.2 Hz beacon at H1 + case 1: + strobe(hi_modes[0], 500, 4500); + break; + // 4 Hz tactical strobe at H1 + case 2: + strobe(hi_modes[0], 83, 167); + break; + // 19 Hz tactical strobe at H1 + case 3: + strobe(hi_modes[0], 17, 35); + break; + } + } + */ + + #ifdef USE_BATTCHECK + else if (current_state == battcheck_state) { + nice_delay_ms(500); // wait a moment to measure voltage + battcheck(); + set_state(base_off_state, 0); + } + #endif +} + + diff --git a/ui/momentary/momentary.c b/ui/momentary/momentary.c new file mode 100644 index 0000000..c7a8cf1 --- /dev/null +++ b/ui/momentary/momentary.c @@ -0,0 +1,80 @@ +/* + * Momentary: Very simple example UI for SpaghettiMonster. + * Is intended to be the simplest possible FSM e-switch UI. + * The light is on while the button is held; off otherwise. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "hwdef-Emisar_D4.h" +#define USE_LVP +#define USE_DEBUG_BLINK +#include "spaghetti-monster.h" + +volatile uint8_t brightness; +volatile uint8_t on_now; + +void light_on() { + on_now = 1; + PWM1_LVL = brightness; + PWM2_LVL = 0; +} + +void light_off() { + on_now = 0; + PWM1_LVL = 0; + PWM2_LVL = 0; +} + +uint8_t momentary_state(Event event, uint16_t arg) { + + if (event == EV_click1_press) { + brightness = 255; + light_on(); + empty_event_sequence(); // don't attempt to parse multiple clicks + return 0; + } + + else if (event == EV_release) { + light_off(); + empty_event_sequence(); // don't attempt to parse multiple clicks + go_to_standby = 1; // sleep while light is off + return 0; + } + + return 1; // event not handled +} + +// LVP / low-voltage protection +void low_voltage() { + if (brightness > 0) { + debug_blink(3); + brightness >>= 1; + if (on_now) light_on(); + } else { + debug_blink(8); + light_off(); + go_to_standby = 1; + } +} + +void setup() { + debug_blink(2); + push_state(momentary_state, 0); +} + +void loop() { } + diff --git a/ui/ramping-ui/ramping-ui.c b/ui/ramping-ui/ramping-ui.c new file mode 100644 index 0000000..583498a --- /dev/null +++ b/ui/ramping-ui/ramping-ui.c @@ -0,0 +1,359 @@ +/* + * Ramping-UI: Ramping UI for SpaghettiMonster. + * + * Copyright (C) 2017 Selene ToyKeeper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "hwdef-Emisar_D4.h" +#define USE_LVP +#define USE_THERMAL_REGULATION +#define DEFAULT_THERM_CEIL 32 +#define USE_DELAY_MS +#define USE_DELAY_ZERO +#define USE_RAMPING +#define USE_BATTCHECK +#define BATTCHECK_VpT +#define RAMP_LENGTH 150 +#include "spaghetti-monster.h" + +// FSM states +uint8_t off_state(Event event, uint16_t arg); +uint8_t steady_state(Event event, uint16_t arg); +uint8_t strobe_state(Event event, uint16_t arg); +#ifdef USE_BATTCHECK +uint8_t battcheck_state(Event event, uint16_t arg); +uint8_t tempcheck_state(Event event, uint16_t arg); +#endif + +// brightness control +uint8_t memorized_level = MAX_1x7135; +// smooth vs discrete ramping +uint8_t ramp_step_size = 1; + +#ifdef USE_THERMAL_REGULATION +// brightness before thermal step-down +uint8_t target_level = 0; +#endif + +// strobe timing +volatile uint8_t strobe_delay = 67; +volatile uint8_t strobe_type = 0; // 0 == party strobe, 1 == tactical strobe + + +uint8_t off_state(Event event, uint16_t arg) { + // turn emitter off when entering state + if (event == EV_enter_state) { + set_level(0); + // sleep while off (lower power use) + go_to_standby = 1; + return EVENT_HANDLED; + } + // hold (initially): go to lowest level, but allow abort for regular click + else if (event == EV_click1_press) { + set_level(1); + return EVENT_HANDLED; + } + // 1 click (before timeout): go to memorized level, but allow abort for double click + else if (event == EV_click1_release) { + set_level(memorized_level); + return EVENT_HANDLED; + } + // 1 click: regular mode + else if (event == EV_1click) { + set_state(steady_state, memorized_level); + return EVENT_HANDLED; + } + // 2 clicks (initial press): off, to prep for later events + else if (event == EV_click2_press) { + set_level(0); + return EVENT_HANDLED; + } + // 2 clicks: highest mode + else if (event == EV_2clicks) { + set_state(steady_state, MAX_LEVEL); + return EVENT_HANDLED; + } + // 3 clicks: strobe mode + else if (event == EV_3clicks) { + set_state(strobe_state, 0); + return EVENT_HANDLED; + } + #ifdef USE_BATTCHECK + // 4 clicks: battcheck mode + else if (event == EV_4clicks) { + set_state(battcheck_state, 0); + return EVENT_HANDLED; + } + #endif + // hold: go to lowest level + else if (event == EV_click1_hold) { + // don't start ramping immediately; + // give the user time to release at moon level + if (arg >= HOLD_TIMEOUT) + set_state(steady_state, 1); + return EVENT_HANDLED; + } + // hold, release quickly: go to lowest level + else if (event == EV_click1_hold_release) { + set_state(steady_state, 1); + return EVENT_HANDLED; + } + // click, hold: go to highest level (for ramping down) + else if (event == EV_click2_hold) { + set_state(steady_state, MAX_LEVEL); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + + +uint8_t steady_state(Event event, uint16_t arg) { + // turn LED on when we first enter the mode + if (event == EV_enter_state) { + // remember this level, unless it's moon or turbo + if ((arg > 1) && (arg < MAX_LEVEL)) + memorized_level = arg; + // use the requested level even if not memorized + #ifdef USE_THERMAL_REGULATION + target_level = arg; + #endif + set_level(arg); + return EVENT_HANDLED; + } + // 1 click: off + else if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: go to/from highest level + else if (event == EV_2clicks) { + if (actual_level < MAX_LEVEL) { + memorized_level = actual_level; // in case we're on moon + #ifdef USE_THERMAL_REGULATION + target_level = MAX_LEVEL; + #endif + set_level(MAX_LEVEL); + } + else { + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #endif + set_level(memorized_level); + } + return EVENT_HANDLED; + } + // 3 clicks: go to strobe modes + else if (event == EV_3clicks) { + set_state(strobe_state, 0); + return EVENT_HANDLED; + } + // 4 clicks: toggle smooth vs discrete ramping + else if (event == EV_4clicks) { + if (ramp_step_size == 1) ramp_step_size = MAX_LEVEL/6; + else ramp_step_size = 1; + set_level(0); + delay_4ms(20/4); + set_level(memorized_level); + return EVENT_HANDLED; + } + // hold: change brightness (brighter) + else if (event == EV_click1_hold) { + // ramp slower in discrete mode + if (arg % ramp_step_size != 0) { + return EVENT_HANDLED; + } + // FIXME: make it ramp down instead, if already at max + if (actual_level + ramp_step_size < MAX_LEVEL) + memorized_level = actual_level + ramp_step_size; + else memorized_level = MAX_LEVEL; + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #endif + // only blink once for each threshold + if ((memorized_level != actual_level) + && ((memorized_level == MAX_1x7135) + || (memorized_level == MAX_LEVEL))) { + set_level(0); + delay_4ms(8/4); + } + set_level(memorized_level); + return EVENT_HANDLED; + } + // click, hold: change brightness (dimmer) + else if (event == EV_click2_hold) { + // ramp slower in discrete mode + if (arg % ramp_step_size != 0) { + return EVENT_HANDLED; + } + // FIXME: make it ramp up instead, if already at min + if (actual_level > ramp_step_size) + memorized_level = (actual_level-ramp_step_size); + else + memorized_level = 1; + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #endif + // only blink once for each threshold + if ((memorized_level != actual_level) + && ((memorized_level == MAX_1x7135) + || (memorized_level == 1))) { + set_level(0); + delay_4ms(8/4); + } + set_level(memorized_level); + return EVENT_HANDLED; + } + #ifdef USE_THERMAL_REGULATION + // TODO: test this on a real light + // overheating: drop by an amount proportional to how far we are above the ceiling + else if (event == EV_temperature_high) { + if (actual_level > MAX_LEVEL/4) { + uint8_t stepdown = actual_level - arg; + if (stepdown < MAX_LEVEL/4) stepdown = MAX_LEVEL/4; + set_level(stepdown); + } + return EVENT_HANDLED; + } + // underheating: increase slowly if we're lower than the target + // (proportional to how low we are) + else if (event == EV_temperature_low) { + if (actual_level < target_level) { + uint8_t stepup = actual_level + (arg>>1); + if (stepup > target_level) stepup = target_level; + set_level(stepup); + } + return EVENT_HANDLED; + } + #endif + return EVENT_NOT_HANDLED; +} + + +uint8_t strobe_state(Event event, uint16_t arg) { + if (event == EV_enter_state) { + return EVENT_HANDLED; + } + // 1 click: off + else if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: toggle party strobe vs tactical strobe + else if (event == EV_2clicks) { + strobe_type ^= 1; + return EVENT_HANDLED; + } + // 3 clicks: go back to regular modes + else if (event == EV_3clicks) { + set_state(steady_state, memorized_level); + return EVENT_HANDLED; + } + // hold: change speed (go faster) + else if (event == EV_click1_hold) { + if ((arg & 1) == 0) { + if (strobe_delay > 8) strobe_delay --; + } + return EVENT_HANDLED; + } + // click, hold: change speed (go slower) + else if (event == EV_click2_hold) { + if ((arg & 1) == 0) { + if (strobe_delay < 255) strobe_delay ++; + } + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + + +#ifdef USE_BATTCHECK +uint8_t battcheck_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: tempcheck mode + else if (event == EV_2clicks) { + set_state(tempcheck_state, 0); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + +uint8_t tempcheck_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} +#endif + + +void low_voltage() { + // "step down" from strobe to something low + if (current_state == strobe_state) { + set_state(steady_state, RAMP_SIZE/6); + } + // in normal mode, step down by half or turn off + else if (current_state == steady_state) { + if (actual_level > 1) { + set_level(actual_level >> 1); + } + else { + set_state(off_state, 0); + } + } + // all other modes, just turn off when voltage is low + else { + set_state(off_state, 0); + } +} + + +void setup() { + set_level(RAMP_SIZE/8); + delay_4ms(3); + set_level(0); + + push_state(off_state, 0); +} + + +void loop() { + if (current_state == strobe_state) { + set_level(MAX_LEVEL); + if (strobe_type == 0) { // party strobe + if (strobe_delay < 30) delay_zero(); + else delay_ms(1); + } else { //tactical strobe + nice_delay_ms(strobe_delay >> 1); + } + set_level(0); + nice_delay_ms(strobe_delay); + } + #ifdef USE_BATTCHECK + else if (current_state == battcheck_state) { + battcheck(); + } + else if (current_state == tempcheck_state) { + blink_num(temperature); + nice_delay_ms(1000); + } + #endif +} diff --git a/ui/rampingios/Makefile b/ui/rampingios/Makefile new file mode 100644 index 0000000..8db198e --- /dev/null +++ b/ui/rampingios/Makefile @@ -0,0 +1,7 @@ +all: + ./build-all.sh + +clean: + rm -f *.hex cfg-*.h *~ *.elf *.o + +.phony: clean diff --git a/ui/rampingios/build-all.sh b/ui/rampingios/build-all.sh new file mode 100755 index 0000000..106dc15 --- /dev/null +++ b/ui/rampingios/build-all.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +cp -av ../anduril/cfg-emisar*.h . + +UI=rampingiosv3 + +for TARGET in cfg-*.h ; do + NAME=$(echo "$TARGET" | perl -ne '/cfg-(.*).h/ && print "$1\n";') + echo "===== $NAME =====" + echo ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}" + ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}" + mv -f "$UI".hex "$UI".$NAME.hex +done diff --git a/ui/rampingios/rampingios-v3.html b/ui/rampingios/rampingios-v3.html new file mode 100644 index 0000000..f72d1ec --- /dev/null +++ b/ui/rampingios/rampingios-v3.html @@ -0,0 +1,501 @@ + + + + + + + RampingIOS V3 Manual :: Phil! Gold + + + + + + + + + + + + + + + + + + + +
+

Tue, 28 Aug 2018

+ +
+

RampingIOS V3 Manual

+ +
+ +

+ + + + +
RampingIOS V3 UI diagram
+

+ +

The Emisar D4S flashlights use a firmware named RampingIOS +V3. (The Emisar D4, D1, and D1S +all use RampingIOS V2.) There's not really a manual; the +only thing we get is the diagram on the right. It's reasonably +comprehensive, but there's a fair amount of detail it merely summarizes, +so I thought a textual manual would be nice.

+ +

The Emisar D4S only works when the head and tailcap are tightened fully. +You can physically lock it out--prevent it from turning on +accidentally--by simply loosening the tailcap a small amount. A quarter +turn will do it.

+ +

Emisar lights are known for their ramping interfaces. Rather than have a +small number of distinct brightness levels, they can vary their brightness +anywhere between their lowest and highest levels, like a light on a +dimmer. The D4S is in ramping mode by default, but it also has a stepped +mode that can be configured to be closer to how non-ramping lights work.

+ +

Each mode--ramping and stepped--can have differently-configured brightness +floors and ceilings.

+ +

The driver for the D4S has two different chipsets. At low brightness +levels, a fairly-efficient but low-power chipset (called a 7135) is +used. These lowest brightness levels are called the "regulated levels". +Each regulated level will always be the same brightness regardless of how +much charge the battery has. Above a particular brightness level, the +light switches over to a less-efficient but high-power chipset (called a +FET). These levels are called "direct-drive". The brightness of the +direct-drive levels is directly related to the battery's charge level; the +more charged the battery, the brighter the levels. The light is at its +most efficient, in terms of power used for every lumen generated, at the +brightest regulated level. When the light is first powered by tightening +the tailcap, it will default to this level.

+ +

At higher brightness levels, the light's LEDs generate a lot of heat. If +the light exceeds its configured maximum temperature, it will begin +dimming itself automatically until the temperature drops below the allowed +maximum.

+ +

The D4S has a set of cyan-colored auxiliary LEDs that can be on when the +main LEDs are off. You can configure the behavior of the aux LEDs.

+ +

Basic Usage

+ +

The default mode for the light is ramping mode. Triple-pressing the +button (3 clicks) while the light is on will toggle between ramping +and stepped mode.

+ +

While the light is off, press and release the button (1 click) to turn +it on. It will turn on at the last-used brightness level. (This is +called "mode memory".) Immediately after loosening and tightening the +tailcap (or after changing the battery), the memorized level will be the +light's max regulated level.

+ +

When the light is on, 1 click will turn it off. The current brightness +level will be memorized for future use. There's a fraction of a second +delay between pressing the button and the light actually turning off. +That's because of the way the light processes input; it's waiting to make +sure you're only going to press the button once (since multiple presses +will trigger other actions).

+ +

When the light is on, holding the button down will brighten the light. In +ramping mode, the brightness will increase gradually ("ramping up"). In +stepped mode, the light will jump through increasing brightness levels. +If you press, release, and then hold the button, it will begin dimming. +In ramping mode, the brightness will decrease gradually ("ramping +down"). In stepped mode, the light will jump through decreasing +brightness levels. While the light is changing, if you release the button +and immediately hold it again, the direction (dimming or brightening) will +switch.

+ +

In ramping mode, while the light is ramping, it'll briefly blink off and +on again at two different brightness levels: the maximum regulated level +and the brightness ceiling.

+ +

While the light is off, double-pressing the button (2 clicks) will +immediately jump to the brightness ceiling.

+ +

While the light is on, 2 clicks will jump to the maximum brightness +level, regardless of the configured brightness ceiling. Another two +clicks will go back to the previous brightness level.

+ +

While the light is off, if you hold the button the light will turn on at +its lowest level. If you continue holding the button, the light will +begin brightening from there.

+ +
Configuration Menus
+ +

The light has several different configuration modes. Each of those modes +works more or less the same way. The mode will have a series of menu +items that it will go through. For each menu item, the light will first +blink a number of times corresponding to the item number (first, second, +etc.) After that, the light will begin fluttering on and off fairly +quickly. While the light is fluttering, you can click the button a number +of times; the light will count the number of button presses and use that +number as its new configuration for that menu item. After a short period +of time, the fluttering will stop and the light will move on to the next +menu item. After the light has gone through all of the menu items, it +will return to whatever mode it was in before entering the configuration +mode.

+ +

If you don't press the button during a particular menu item's fluttering, +that item will remain unchanged.

+ +
Configuring the Basic Modes
+ +

While the light is on, 4 clicks will enter ramping or stepped +configuration mode, depending on which mode the light was in before the 4 +clicks.

+ +

For ramping mode, there are two menu options:

+ +
    +
  1. Brightness floor (default 1/150)
  2. +
  3. Brightness ceiling (default 150/150)
  4. +
+ +

During the floor configuration, press the button equal to the number of +ramping levels (out of 150) at which the floor should be. To set the +lowest possible floor, click the button once.

+ +

The ceiling is configured similarly, but you press the button equal to the +number of steps away from maximum brightness. To set the highest possible +ceiling (at max brightness), click the button once.

+ +

For stepped mode, there are three menu options:

+ +
    +
  1. Brightness floor (default 20/150)
  2. +
  3. Brightness ceiling (default 120/150)
  4. +
  5. Number of steps (default 7)
  6. +
+ +

Other Modes

+ +

The other modes largely involve multiple clicks from off. Most of them +are not generally needed for everyday use, but they supplement the light's +basic operations.

+ +
BattCheck/TempCheck Modes
+ +

From off, 3 clicks will enter "BattCheck" mode, which blinks out the +current battery voltage. First it blinks the number of volts, then it +pauses, then it blinks out the tenths of volts. Thus, if the battery were +at 3.5 volts, the light would blink three times, pause, then five times. +For zeroes, it gives a very short blink.

+ +

A fully-charged lithium-ion battery is 4.2 volts. The light considers 2.8 +volts to be an empty battery and won't turn on if the battery is at or +below 2.8 volts.

+ +

The voltage sequence will continue blinking until you turn off the light +with a single click.

+ +

While the light is in BattCheck mode, 2 clicks will enter TempCheck +mode. Instead of blinking out the battery voltage, the light will start +blinking out its current temperature in degrees Celsius, first the tens +digit then the units digit. Like BattCheck mode, the light will continue +blinking out the temperature until you turn it off with a single click.

+ +

While the light is in TempCheck mode, 4 clicks will enter thermal +configuration mode. See the thermal configuration mode documentation +below for how that works.

+ +
Tactical Mode
+ +

From off, 4 clicks will enter "tactical" or "momentary" mode. The +light will flash once to show that it's entered the mode. The auxiliary +LEDs will turn off (if they were on). In tactical mode, the light will +turn on at its memorized brightness for as long as the button is being +held down. It will turn off as soon as the button is released.

+ +

There's no button press combination that will exit tactical mode. To exit +it, you will have to partially unscrew and retighten the tailcap.

+ +
Lockout Mode
+ +

From off, 6 clicks will enter lockout mode. The light will flash +twice to show that it's entered the mode. There's a separate aux LED mode +for lockout mode, so you can tell whether the light is in lockout or not.

+ +

In lockout mode, pressing the button will turn on the light at its lowest +brightness ("moonlight mode") for as long as the button is held down.

+ +

Another 6 clicks will exit lockout mode. The light will flash twice to +show that it's left the mode.

+ +

While in lockout mode, 3 clicks will cycle through the various +settings for the aux LEDs in lockout mode. The four modes are, in order: +low, high, blink (on high), and off. The default mode is blink.

+ +

Remember that loosening the tailcap a quarter turn will also lock out the +light. Using the 6 clicks is called "electronic lockout", while turning +the tailcap is "physical lockout".

+ +
Aux LED Configuration
+ +

From off, 7 clicks will cycle to the next aux LED mode. The four +modes are, in order: low, high, blink (on high), and off. The default +mode is low.

+ +
Beacon Mode
+ +

From off, 8 clicks will enter beacon mode. In beacon mode, the light +will blink on and off every few seconds.

+ +

By default, the light will blink every two seconds. To change the timing, +use 4 clicks while in beacon mode. The light will enter a one-item +menu. During the flickering for input, press the button a number of times +equal to the number of seconds between blinks.

+ +

1 click will exit beacon mode.

+ +
Thermal Configuration Mode
+ +

From off, 10 clicks will enter thermal configuration mode.

+ +

The menu items here are:

+ +
    +
  1. Current temperature (every click is one degree Celsius)
  2. +
  3. Temperature ceiling (every click is one degree above 30°C)
  4. +
+ +

The "current temperature" item can be used to adjust the calibration of +the light's temperature sensor. To use it, make sure the light has been +off long enough that all of its components have cooled (or warmed) to the +ambient temperature. Check the ambient temperature using a thermometer +you trust. Go to thermal configuration mode, and enter the current +temperature by clicking the button a number of times equal to the +temperature in degrees Celsius. (If it's 22°C, click the button 22 +times.)

+ +

You can check the default calibration by entering TempCheck mode from a +room-temperature light. The D4Ss are supposed to go through a temperature +calibration at the factory, so hopefully most of them won't need manual +thermal calibration.

+ +

The temperature ceiling is simply the highest temperature the light should +be allowed to reach. Once it hits its temperature ceiling, it will +progressively dim itself until the temperature stabilizes below the +ceiling. Note that the number of clicks in that menu option is added to +30 to reach the actual ceiling. (Thus, you can't set a ceiling below +31°C.) The maximum allowed ceiling is 70°C.

+ +

The default temperature ceiling is 45°C.

+ + +
+ + + +

+
Phil! Gold
+ + + + diff --git a/ui/rampingios/rampingios-v3.md b/ui/rampingios/rampingios-v3.md new file mode 100644 index 0000000..bc0e2b7 --- /dev/null +++ b/ui/rampingios/rampingios-v3.md @@ -0,0 +1,262 @@ +RampingIOS V3 Manual + +This Markdown-formatted manual was contributed by phil_g under a +Creative Commons CC0 waiver: + http://aperiodic.net/phil/archives/Geekery/rampingios-v3.html + https://creativecommons.org/publicdomain/zero/1.0/ + + +
+ + + + +
RampingIOS V3 UI diagram
+
+ +The Emisar [D4S][emisar-d4s] flashlights use a firmware named RampingIOS +V3. (The Emisar [D4][emisar-d4], [D1][emisar-d1], and [D1S][emisar-d1s] +all use [RampingIOS V2][rampingios-v2].) There's not really a manual; the +only thing we get is the diagram on the right. It's reasonably +comprehensive, but there's a fair amount of detail it merely summarizes, +so I thought a textual manual would be nice. + + [emisar-d4]: https://intl-outdoor.com/emisar-d4-high-power-led-flashlight-p-921.html + [emisar-d1]: https://intl-outdoor.com/emisar-d1-mini-thrower-p-922.html + [emisar-d1s]: https://intl-outdoor.com/emisar-d1s-thrower-p-926.html + [emisar-d4s]: https://intl-outdoor.com/emisar-d4s-26650-high-power-led-flashlight-p-932.html + [rampingios-v2]: http://aperiodic.net/phil/archives/Geekery/rampingios-v2.html + +The Emisar D4S only works when the head and tailcap are tightened fully. +You can physically lock it out--prevent it from turning on +accidentally--by simply loosening the tailcap a small amount. A quarter +turn will do it. + +Emisar lights are known for their ramping interfaces. Rather than have a +small number of distinct brightness levels, they can vary their brightness +anywhere between their lowest and highest levels, like a light on a +dimmer. The D4S is in ramping mode by default, but it also has a stepped +mode that can be configured to be closer to how non-ramping lights work. + +Each mode--ramping and stepped--can have differently-configured brightness +floors and ceilings. + +The driver for the D4S has two different chipsets. At low brightness +levels, a fairly-efficient but low-power chipset (called a *7135*) is +used. These lowest brightness levels are called the "*regulated levels*". +Each regulated level will always be the same brightness regardless of how +much charge the battery has. Above a particular brightness level, the +light switches over to a less-efficient but high-power chipset (called a +*FET*). These levels are called "*direct-drive*". The brightness of the +direct-drive levels is directly related to the battery's charge level; the +more charged the battery, the brighter the levels. The light is at its +most efficient, in terms of power used for every lumen generated, at the +brightest regulated level. When the light is first powered by tightening +the tailcap, it will default to this level. + +At higher brightness levels, the light's LEDs generate a lot of heat. If +the light exceeds its configured maximum temperature, it will begin +dimming itself automatically until the temperature drops below the allowed +maximum. + +The D4S has a set of cyan-colored auxiliary LEDs that can be on when the +main LEDs are off. You can configure the behavior of the aux LEDs. + +#### Basic Usage + +The default mode for the light is ramping mode. Triple-pressing the +button (**3 clicks**) while the light is on will toggle between ramping +and stepped mode. + +While the light is off, press and release the button (**1 click**) to turn +it on. It will turn on at the last-used brightness level. (This is +called "*mode memory*".) Immediately after loosening and tightening the +tailcap (or after changing the battery), the memorized level will be the +light's max regulated level. + +When the light is on, 1 click will turn it off. The current brightness +level will be memorized for future use. There's a fraction of a second +delay between pressing the button and the light actually turning off. +That's because of the way the light processes input; it's waiting to make +sure you're only going to press the button once (since multiple presses +will trigger other actions). + +When the light is on, holding the button down will brighten the light. In +ramping mode, the brightness will increase gradually ("*ramping up*"). In +stepped mode, the light will jump through increasing brightness levels. +If you press, release, and then hold the button, it will begin dimming. +In ramping mode, the brightness will decrease gradually ("*ramping +down*"). In stepped mode, the light will jump through decreasing +brightness levels. While the light is changing, if you release the button +and immediately hold it again, the direction (dimming or brightening) will +switch. + +In ramping mode, while the light is ramping, it'll briefly blink off and +on again at two different brightness levels: the maximum regulated level +and the brightness ceiling. + +While the light is off, double-pressing the button (**2 clicks**) will +immediately jump to the brightness ceiling. + +While the light is on, **2 clicks** will jump to the maximum brightness +level, regardless of the configured brightness ceiling. Another two +clicks will go back to the previous brightness level. + +While the light is off, if you hold the button the light will turn on at +its lowest level. If you continue holding the button, the light will +begin brightening from there. + +##### Configuration Menus + +The light has several different configuration modes. Each of those modes +works more or less the same way. The mode will have a series of menu +items that it will go through. For each menu item, the light will first +blink a number of times corresponding to the item number (first, second, +etc.) After that, the light will begin fluttering on and off fairly +quickly. While the light is fluttering, you can click the button a number +of times; the light will count the number of button presses and use that +number as its new configuration for that menu item. After a short period +of time, the fluttering will stop and the light will move on to the next +menu item. After the light has gone through all of the menu items, it +will return to whatever mode it was in before entering the configuration +mode. + +If you don't press the button during a particular menu item's fluttering, +that item will remain unchanged. + +##### Configuring the Basic Modes + +While the light is on, **4 clicks** will enter ramping or stepped +configuration mode, depending on which mode the light was in before the 4 +clicks. + +For ramping mode, there are two menu options: + + 1. Brightness floor (default 1/150) + 2. Brightness ceiling (default 150/150) + +During the floor configuration, press the button equal to the number of +ramping levels (out of 150) at which the floor should be. To set the +lowest possible floor, click the button once. + +The ceiling is configured similarly, but you press the button equal to the +number of steps away from maximum brightness. To set the highest possible +ceiling (at max brightness), click the button once. + +For stepped mode, there are three menu options: + + 1. Brightness floor (default 20/150) + 2. Brightness ceiling (default 120/150) + 3. Number of steps (default 7) + +#### Other Modes + +The other modes largely involve multiple clicks from off. Most of them +are not generally needed for everyday use, but they supplement the light's +basic operations. + +##### BattCheck/TempCheck Modes + +From off, **3 clicks** will enter "BattCheck" mode, which blinks out the +current battery voltage. First it blinks the number of volts, then it +pauses, then it blinks out the tenths of volts. Thus, if the battery were +at 3.5 volts, the light would blink three times, pause, then five times. +For zeroes, it gives a very short blink. + +A fully-charged lithium-ion battery is 4.2 volts. The light considers 2.8 +volts to be an empty battery and won't turn on if the battery is at or +below 2.8 volts. + +The voltage sequence will continue blinking until you turn off the light +with a single click. + +While the light is in BattCheck mode, **2 clicks** will enter TempCheck +mode. Instead of blinking out the battery voltage, the light will start +blinking out its current temperature in degrees Celsius, first the tens +digit then the units digit. Like BattCheck mode, the light will continue +blinking out the temperature until you turn it off with a single click. + +While the light is in TempCheck mode, **4 clicks** will enter thermal +configuration mode. See the thermal configuration mode documentation +below for how that works. + +##### Tactical Mode + +From off, **4 clicks** will enter "tactical" or "momentary" mode. The +light will flash once to show that it's entered the mode. The auxiliary +LEDs will turn off (if they were on). In tactical mode, the light will +turn on at its memorized brightness for as long as the button is being +held down. It will turn off as soon as the button is released. + +There's no button press combination that will exit tactical mode. To exit +it, you will have to partially unscrew and retighten the tailcap. + +##### Lockout Mode + +From off, **6 clicks** will enter lockout mode. The light will flash +twice to show that it's entered the mode. There's a separate aux LED mode +for lockout mode, so you can tell whether the light is in lockout or not. + +In lockout mode, pressing the button will turn on the light at its lowest +brightness ("*moonlight mode*") for as long as the button is held down. + +Another 6 clicks will exit lockout mode. The light will flash twice to +show that it's left the mode. + +While in lockout mode, **3 clicks** will cycle through the various +settings for the aux LEDs in lockout mode. The four modes are, in order: +low, high, blink (on high), and off. The default mode is blink. + +Remember that loosening the tailcap a quarter turn will also lock out the +light. Using the 6 clicks is called "*electronic lockout*", while turning +the tailcap is "*physical lockout*". + +##### Aux LED Configuration + +From off, **7 clicks** will cycle to the next aux LED mode. The four +modes are, in order: low, high, blink (on high), and off. The default +mode is low. + +##### Beacon Mode + +From off, **8 clicks** will enter beacon mode. In beacon mode, the light +will blink on and off every few seconds. + +By default, the light will blink every two seconds. To change the timing, +use **4 clicks** while in beacon mode. The light will enter a one-item +menu. During the flickering for input, press the button a number of times +equal to the number of seconds between blinks. + +1 click will exit beacon mode. + +##### Thermal Configuration Mode + +From off, **10 clicks** will enter thermal configuration mode. + +The menu items here are: + + 1. Current temperature (every click is one degree Celsius) + 2. Temperature ceiling (every click is one degree *above 30°C*) + +The "current temperature" item can be used to adjust the calibration of +the light's temperature sensor. To use it, make sure the light has been +off long enough that all of its components have cooled (or warmed) to the +ambient temperature. Check the ambient temperature using a thermometer +you trust. Go to thermal configuration mode, and enter the current +temperature by clicking the button a number of times equal to the +temperature in degrees Celsius. (If it's 22°C, click the button 22 +times.) + +You can check the default calibration by entering TempCheck mode from a +room-temperature light. The D4Ss are supposed to go through a temperature +calibration at the factory, so hopefully most of them won't need manual +thermal calibration. + +The temperature ceiling is simply the highest temperature the light should +be allowed to reach. Once it hits its temperature ceiling, it will +progressively dim itself until the temperature stabilizes below the +ceiling. Note that the number of clicks in that menu option is added to +*30* to reach the actual ceiling. (Thus, you can't set a ceiling below +31°C.) The maximum allowed ceiling is 70°C. + +The default temperature ceiling is 45°C. diff --git a/ui/rampingios/rampingios-v3.txt b/ui/rampingios/rampingios-v3.txt new file mode 100644 index 0000000..4598a76 --- /dev/null +++ b/ui/rampingios/rampingios-v3.txt @@ -0,0 +1,324 @@ +RampingIOS V3 Manual +http://aperiodic.net/phil/archives/Geekery/rampingios-v3.html + +Tue, 28 Aug 2018 +9:47AM | Geekery | # + + +RampingIOS V3 Manual +-------------------- + +[rampingiosv3-ui.png] RampingIOS V3 UI diagram + +The Emisar D4S flashlights use a firmware named RampingIOS V3. (The Emisar D4, +D1, and D1S all use RampingIOS V2.) There's not really a manual; the only thing +we get is the diagram on the right. It's reasonably comprehensive, but there's +a fair amount of detail it merely summarizes, so I thought a textual manual +would be nice. + +The Emisar D4S only works when the head and tailcap are tightened fully. You +can physically lock it out--prevent it from turning on accidentally--by simply +loosening the tailcap a small amount. A quarter turn will do it. + +Emisar lights are known for their ramping interfaces. Rather than have a small +number of distinct brightness levels, they can vary their brightness anywhere +between their lowest and highest levels, like a light on a dimmer. The D4S is +in ramping mode by default, but it also has a stepped mode that can be +configured to be closer to how non-ramping lights work. + +Each mode--ramping and stepped--can have differently-configured brightness +floors and ceilings. + +The driver for the D4S has two different chipsets. At low brightness levels, a +fairly-efficient but low-power chipset (called a 7135) is used. These lowest +brightness levels are called the "regulated levels". Each regulated level will +always be the same brightness regardless of how much charge the battery has. +Above a particular brightness level, the light switches over to a +less-efficient but high-power chipset (called a FET). These levels are called " +direct-drive". The brightness of the direct-drive levels is directly related to +the battery's charge level; the more charged the battery, the brighter the +levels. The light is at its most efficient, in terms of power used for every +lumen generated, at the brightest regulated level. When the light is first +powered by tightening the tailcap, it will default to this level. + +At higher brightness levels, the light's LEDs generate a lot of heat. If the +light exceeds its configured maximum temperature, it will begin dimming itself +automatically until the temperature drops below the allowed maximum. + +The D4S has a set of cyan-colored auxiliary LEDs that can be on when the main +LEDs are off. You can configure the behavior of the aux LEDs. + + +Basic Usage +----------- + +The default mode for the light is ramping mode. Triple-pressing the button (3 +clicks) while the light is on will toggle between ramping and stepped mode. + +While the light is off, press and release the button (1 click) to turn it on. +It will turn on at the last-used brightness level. (This is called "mode memory +".) Immediately after loosening and tightening the tailcap (or after changing +the battery), the memorized level will be the light's max regulated level. + +When the light is on, 1 click will turn it off. The current brightness level +will be memorized for future use. There's a fraction of a second delay between +pressing the button and the light actually turning off. That's because of the +way the light processes input; it's waiting to make sure you're only going to +press the button once (since multiple presses will trigger other actions). + +When the light is on, holding the button down will brighten the light. In +ramping mode, the brightness will increase gradually ("ramping up"). In stepped +mode, the light will jump through increasing brightness levels. If you press, +release, and then hold the button, it will begin dimming. In ramping mode, the +brightness will decrease gradually ("ramping down"). In stepped mode, the light +will jump through decreasing brightness levels. While the light is changing, if +you release the button and immediately hold it again, the direction (dimming or +brightening) will switch. + +In ramping mode, while the light is ramping, it'll briefly blink off and on +again at two different brightness levels: the maximum regulated level and the +brightness ceiling. + +While the light is off, double-pressing the button (2 clicks) will immediately +jump to the brightness ceiling. + +While the light is on, 2 clicks will jump to the maximum brightness level, +regardless of the configured brightness ceiling. Another two clicks will go +back to the previous brightness level. + +While the light is off, if you hold the button the light will turn on at its +lowest level. If you continue holding the button, the light will begin +brightening from there. + + +Configuration Menus +------------------- + +The light has several different configuration modes. Each of those modes works +more or less the same way. The mode will have a series of menu items that it +will go through. For each menu item, the light will first blink a number of +times corresponding to the item number (first, second, etc.) After that, the +light will begin fluttering on and off fairly quickly. While the light is +fluttering, you can click the button a number of times; the light will count +the number of button presses and use that number as its new configuration for +that menu item. After a short period of time, the fluttering will stop and the +light will move on to the next menu item. After the light has gone through all +of the menu items, it will return to whatever mode it was in before entering +the configuration mode. + +If you don't press the button during a particular menu item's fluttering, that +item will remain unchanged. + + +Configuring the Basic Modes + +While the light is on, 4 clicks will enter ramping or stepped configuration +mode, depending on which mode the light was in before the 4 clicks. + +For ramping mode, there are two menu options: + + 1. Brightness floor (default 1/150) + 2. Brightness ceiling (default 150/150) + +During the floor configuration, press the button equal to the number of ramping +levels (out of 150) at which the floor should be. To set the lowest possible +floor, click the button once. + +The ceiling is configured similarly, but you press the button equal to the +number of steps away from maximum brightness. To set the highest possible +ceiling (at max brightness), click the button once. + +For stepped mode, there are three menu options: + + 1. Brightness floor (default 20/150) + 2. Brightness ceiling (default 120/150) + 3. Number of steps (default 7) + + +Other Modes +----------- + +The other modes largely involve multiple clicks from off. Most of them are not +generally needed for everyday use, but they supplement the light's basic +operations. + + +BattCheck/TempCheck Modes + +From off, 3 clicks will enter "BattCheck" mode, which blinks out the current +battery voltage. First it blinks the number of volts, then it pauses, then it +blinks out the tenths of volts. Thus, if the battery were at 3.5 volts, the +light would blink three times, pause, then five times. For zeroes, it gives a +very short blink. + +A fully-charged lithium-ion battery is 4.2 volts. The light considers 2.8 volts +to be an empty battery and won't turn on if the battery is at or below 2.8 +volts. + +The voltage sequence will continue blinking until you turn off the light with a +single click. + +While the light is in BattCheck mode, 2 clicks will enter TempCheck mode. +Instead of blinking out the battery voltage, the light will start blinking out +its current temperature in degrees Celsius, first the tens digit then the units +digit. Like BattCheck mode, the light will continue blinking out the +temperature until you turn it off with a single click. + +While the light is in TempCheck mode, 4 clicks will enter thermal configuration +mode. See the thermal configuration mode documentation below for how that +works. + + +Tactical Mode + +From off, 4 clicks will enter "tactical" or "momentary" mode. The light will +flash once to show that it's entered the mode. The auxiliary LEDs will turn off +(if they were on). In tactical mode, the light will turn on at its memorized +brightness for as long as the button is being held down. It will turn off as +soon as the button is released. + +There's no button press combination that will exit tactical mode. To exit it, +you will have to partially unscrew and retighten the tailcap. + + +Lockout Mode + +From off, 6 clicks will enter lockout mode. The light will flash twice to show +that it's entered the mode. There's a separate aux LED mode for lockout mode, +so you can tell whether the light is in lockout or not. + +In lockout mode, pressing the button will turn on the light at its lowest +brightness ("moonlight mode") for as long as the button is held down. + +Another 6 clicks will exit lockout mode. The light will flash twice to show +that it's left the mode. + +While in lockout mode, 3 clicks will cycle through the various settings for the +aux LEDs in lockout mode. The four modes are, in order: low, high, blink (on +high), and off. The default mode is blink. + +Remember that loosening the tailcap a quarter turn will also lock out the +light. Using the 6 clicks is called "electronic lockout", while turning the +tailcap is "physical lockout". + + +Aux LED Configuration + +From off, 7 clicks will cycle to the next aux LED mode. The four modes are, in +order: low, high, blink (on high), and off. The default mode is low. + + +Beacon Mode + +From off, 8 clicks will enter beacon mode. In beacon mode, the light will blink +on and off every few seconds. + +By default, the light will blink every two seconds. To change the timing, use 4 +clicks while in beacon mode. The light will enter a one-item menu. During the +flickering for input, press the button a number of times equal to the number of +seconds between blinks. + +1 click will exit beacon mode. + + +Thermal Configuration Mode + +From off, 10 clicks will enter thermal configuration mode. + +The menu items here are: + + 1. Current temperature (every click is one degree Celsius) + 2. Temperature ceiling (every click is one degree above 30?C) + +The "current temperature" item can be used to adjust the calibration of the +light's temperature sensor. To use it, make sure the light has been off long +enough that all of its components have cooled (or warmed) to the ambient +temperature. Check the ambient temperature using a thermometer you trust. Go to +thermal configuration mode, and enter the current temperature by clicking the +button a number of times equal to the temperature in degrees Celsius. (If it's +22?C, click the button 22 times.) + +You can check the default calibration by entering TempCheck mode from a +room-temperature light. The D4Ss are supposed to go through a temperature +calibration at the factory, so hopefully most of them won't need manual thermal +calibration. + +The temperature ceiling is simply the highest temperature the light should be +allowed to reach. Once it hits its temperature ceiling, it will progressively +dim itself until the temperature stabilizes below the ceiling. Note that the +number of clicks in that menu option is added to 30 to reach the actual +ceiling. (Thus, you can't set a ceiling below 31?C.) The maximum allowed +ceiling is 70?C. + +The default temperature ceiling is 45?C. + + +Static + + * zsh prompt + * PGP + * SSH + * MTA + * tutorials + * config files + * desktop + * books I own + * stuff I'm giving away + * Dr. Who eps I have + * bookmarks + * photos + * about + +Directory + + * Root (143) + + Books (32) + + Events (7) + o Burning Man (3) + o Camping (2) + o PDF (2) + + Geekery (36) + o Test (3) + + General (24) + + Links (12) + o Slashdot (1) + + MTA (22) + + Recipes (4) + + Video Games (6) + o FFXI (1) + +Archive + + ?August? +Sun Mon Tue Wed Thu Fri Sat + 1 2 3 4 +5 6 7 8 9 10 11 +12 13 14 15 16 17 18 +19 20 21 22 23 24 25 +26 27 28 29 30 31 + + ?2018? + Months +Jan Feb Mar Apr May Jun +Jul Aug Sep Oct Nov Dec + +Search + +[ ] +Powered by Google + +Currently Reading + +Recent Books + +------------------------------------------------------------------------------- + +Copyright (C) 2018 Phil Gold + +Back to main page. + + * Valid HTML 4.01 + * Valid CSS 2 + * RSS syndication + * Valid RSS 1.0 + diff --git a/ui/rampingios/rampingiosv3-ui.png b/ui/rampingios/rampingiosv3-ui.png new file mode 100644 index 0000000..d02dbf6 Binary files /dev/null and b/ui/rampingios/rampingiosv3-ui.png differ diff --git a/ui/rampingios/rampingiosv3.c b/ui/rampingios/rampingiosv3.c new file mode 100644 index 0000000..e990a5a --- /dev/null +++ b/ui/rampingios/rampingiosv3.c @@ -0,0 +1,1253 @@ +/* + * RampingIOS V3: FSM-based version of RampingIOS V2 UI, with upgrades. + * + * Copyright (C) 2018-2019 Selene ToyKeeper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/********* User-configurable options *********/ +// Anduril config file name (set it here or define it at the gcc command line) +//#define CONFIGFILE cfg-emisar-d4s.h + +#define USE_LVP // FIXME: won't build when this option is turned off + +// parameters for this defined below or per-driver +#define USE_THERMAL_REGULATION +#define DEFAULT_THERM_CEIL 45 // try not to get hotter than this +#define USE_TENCLICK_THERMAL_CONFIG // ten clicks from off -> thermal config mode + +// short blip when crossing from "click" to "hold" from off +// (helps the user hit moon mode exactly, instead of holding too long +// or too short) +#define MOON_TIMING_HINT +// short blips while ramping +#define BLINK_AT_RAMP_MIDDLE +//#define BLINK_AT_RAMP_FLOOR +#define BLINK_AT_RAMP_CEILING +//#define BLINK_AT_STEPS // whenever a discrete ramp mode is passed in smooth mode + +// ramp down via regular button hold if a ramp-up ended <1s ago +// ("hold, release, hold" ramps down instead of up) +#define USE_REVERSING + +// battery readout style (pick one) +#define BATTCHECK_VpT +//#define BATTCHECK_8bars // FIXME: breaks build +//#define BATTCHECK_4bars // FIXME: breaks build + +// enable beacon mode +#define USE_BEACON_MODE + +// make the ramps configurable by the user +#define USE_RAMP_CONFIG + +/***** specific settings for known driver types *****/ +#include "tk.h" +#include incfile(CONFIGFILE) + + +// thermal properties, if not defined per-driver +#ifndef MIN_THERM_STEPDOWN +#define MIN_THERM_STEPDOWN MAX_1x7135 // lowest value it'll step down to +#endif +#ifndef THERM_FASTER_LEVEL + #ifdef MAX_Nx7135 + #define THERM_FASTER_LEVEL MAX_Nx7135 // throttle back faster when high + #else + #define THERM_FASTER_LEVEL (RAMP_SIZE*4/5) // throttle back faster when high + #endif +#endif +#ifdef USE_THERMAL_REGULATION +#define USE_SET_LEVEL_GRADUALLY // isn't used except for thermal adjustments +#endif + + +/********* Configure SpaghettiMonster *********/ +#define USE_DELAY_ZERO +#define USE_RAMPING +#ifndef RAMP_LENGTH +#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file +#endif +#define USE_BATTCHECK +#define USE_IDLE_MODE // reduce power use while awake and no tasks are pending +#define USE_DYNAMIC_UNDERCLOCKING // cut clock speed at very low modes for better efficiency + +// try to auto-detect how many eeprom bytes +#define USE_EEPROM +#define EEPROM_BYTES_BASE 7 + +#ifdef USE_INDICATOR_LED +#define EEPROM_INDICATOR_BYTES 1 +#else +#define EEPROM_INDICATOR_BYTES 0 +#endif + +#ifdef USE_THERMAL_REGULATION +#define EEPROM_THERMAL_BYTES 2 +#else +#define EEPROM_THERMAL_BYTES 0 +#endif + +#define EEPROM_BYTES (EEPROM_BYTES_BASE+EEPROM_INDICATOR_BYTES+EEPROM_THERMAL_BYTES) + + +#include "spaghetti-monster.h" + + +// FSM states +uint8_t off_state(Event event, uint16_t arg); +// simple numeric entry config menu +uint8_t config_state_base(Event event, uint16_t arg, + uint8_t num_config_steps, + void (*savefunc)()); +#define MAX_CONFIG_VALUES 3 +uint8_t config_state_values[MAX_CONFIG_VALUES]; +// ramping mode and its related config mode +uint8_t steady_state(Event event, uint16_t arg); +#ifdef USE_RAMP_CONFIG +uint8_t ramp_config_state(Event event, uint16_t arg); +#endif +#ifdef USE_BATTCHECK +uint8_t battcheck_state(Event event, uint16_t arg); +#endif +#ifdef USE_THERMAL_REGULATION +#define USE_BLINK_NUM +uint8_t tempcheck_state(Event event, uint16_t arg); +uint8_t thermal_config_state(Event event, uint16_t arg); +#endif +#ifdef USE_BEACON_MODE +// beacon mode and its related config mode +uint8_t beacon_state(Event event, uint16_t arg); +uint8_t beacon_config_state(Event event, uint16_t arg); +#endif +// soft lockout +#define MOON_DURING_LOCKOUT_MODE +// if enabled, 2nd lockout click goes to the other ramp's floor level +//#define LOCKOUT_MOON_FANCY +uint8_t lockout_state(Event event, uint16_t arg); +// momentary / signalling mode +uint8_t momentary_state(Event event, uint16_t arg); + +// general helper function for config modes +uint8_t number_entry_state(Event event, uint16_t arg); +// return value from number_entry_state() +volatile uint8_t number_entry_value; + +void blink_confirm(uint8_t num); +#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY) +void indicator_blink(uint8_t arg); +#endif + +// remember stuff even after battery was changed +void load_config(); +void save_config(); + +// default ramp options if not overridden earlier per-driver +#ifndef RAMP_SMOOTH_FLOOR + #define RAMP_SMOOTH_FLOOR 1 +#endif +#ifndef RAMP_SMOOTH_CEIL + #if PWM_CHANNELS == 3 + #define RAMP_SMOOTH_CEIL MAX_Nx7135 + #else + #define RAMP_SMOOTH_CEIL MAX_LEVEL - 30 + #endif +#endif +#ifndef RAMP_DISCRETE_FLOOR + #define RAMP_DISCRETE_FLOOR 20 +#endif +#ifndef RAMP_DISCRETE_CEIL + #define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL +#endif +#ifndef RAMP_DISCRETE_STEPS + #define RAMP_DISCRETE_STEPS 7 +#endif + +// mile marker(s) partway up the ramp +// default: blink only at border between regulated and FET +#ifdef BLINK_AT_RAMP_MIDDLE + #if PWM_CHANNELS >= 3 + #ifndef BLINK_AT_RAMP_MIDDLE_1 + #define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135 + #ifndef BLINK_AT_RAMP_MIDDLE_2 + #define BLINK_AT_RAMP_MIDDLE_2 MAX_1x7135 + #endif + #endif + #else + #ifndef BLINK_AT_RAMP_MIDDLE_1 + #define BLINK_AT_RAMP_MIDDLE_1 MAX_1x7135 + #endif + #endif +#endif + +// brightness control +#ifndef DEFAULT_LEVEL +#define DEFAULT_LEVEL MAX_1x7135 +#endif +uint8_t memorized_level = DEFAULT_LEVEL; +// smooth vs discrete ramping +volatile uint8_t ramp_style = 0; // 0 = smooth, 1 = discrete +volatile uint8_t ramp_smooth_floor = RAMP_SMOOTH_FLOOR; +volatile uint8_t ramp_smooth_ceil = RAMP_SMOOTH_CEIL; +volatile uint8_t ramp_discrete_floor = RAMP_DISCRETE_FLOOR; +volatile uint8_t ramp_discrete_ceil = RAMP_DISCRETE_CEIL; +volatile uint8_t ramp_discrete_steps = RAMP_DISCRETE_STEPS; +uint8_t ramp_discrete_step_size; // don't set this + +#ifdef USE_INDICATOR_LED + // bits 2-3 control lockout mode + // bits 0-1 control "off" mode + // modes are: 0=off, 1=low, 2=high, 3=blinking (if TICK_DURING_STANDBY enabled) + #ifdef INDICATOR_LED_DEFAULT_MODE + uint8_t indicator_led_mode = INDICATOR_LED_DEFAULT_MODE; + #else + #ifdef USE_INDICATOR_LED_WHILE_RAMPING + //uint8_t indicator_led_mode = (1<<2) + 2; + uint8_t indicator_led_mode = (2<<2) + 1; + #else + uint8_t indicator_led_mode = (3<<2) + 1; + #endif + #endif +#endif + +// calculate the nearest ramp level which would be valid at the moment +// (is a no-op for smooth ramp, but limits discrete ramp to only the +// correct levels for the user's config) +uint8_t nearest_level(int16_t target); + +#ifdef USE_THERMAL_REGULATION +// brightness before thermal step-down +uint8_t target_level = 0; +#endif + +#ifdef USE_BEACON_MODE +// beacon timing +volatile uint8_t beacon_seconds = 2; +#endif + + +uint8_t off_state(Event event, uint16_t arg) { + // turn emitter off when entering state + if (event == EV_enter_state) { + set_level(0); + #ifdef USE_INDICATOR_LED + indicator_led(indicator_led_mode & 0x03); + #endif + // sleep while off (lower power use) + go_to_standby = 1; + return EVENT_HANDLED; + } + // go back to sleep eventually if we got bumped but didn't leave "off" state + else if (event == EV_tick) { + if (arg > TICKS_PER_SECOND*2) { + go_to_standby = 1; + #ifdef USE_INDICATOR_LED + indicator_led(indicator_led_mode & 0x03); + #endif + } + return EVENT_HANDLED; + } + #if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED) + // blink the indicator LED, maybe + else if (event == EV_sleep_tick) { + if ((indicator_led_mode & 0b00000011) == 0b00000011) { + indicator_blink(arg); + } + return EVENT_HANDLED; + } + #endif + // hold (initially): go to lowest level (floor), but allow abort for regular click + else if (event == EV_click1_press) { + set_level(nearest_level(1)); + return EVENT_HANDLED; + } + // hold: go to lowest level + else if (event == EV_click1_hold) { + #ifdef MOON_TIMING_HINT + if (arg == 0) { + // let the user know they can let go now to stay at moon + uint8_t temp = actual_level; + set_level(0); + delay_4ms(3); + set_level(temp); + } else + #endif + // don't start ramping immediately; + // give the user time to release at moon level + //if (arg >= HOLD_TIMEOUT) { // smaller + if (arg >= (!ramp_style) * HOLD_TIMEOUT) { // more consistent + set_state(steady_state, 1); + } + return EVENT_HANDLED; + } + // hold, release quickly: go to lowest level (floor) + else if (event == EV_click1_hold_release) { + set_state(steady_state, 1); + return EVENT_HANDLED; + } + // 1 click (before timeout): go to memorized level, but allow abort for double click + else if (event == EV_click1_release) { + set_level(nearest_level(memorized_level)); + return EVENT_HANDLED; + } + // 1 click: regular mode + else if (event == EV_1click) { + set_state(steady_state, memorized_level); + return EVENT_HANDLED; + } + // click, hold: go to highest level (ceiling) (for ramping down) + else if (event == EV_click2_hold) { + set_state(steady_state, MAX_LEVEL); + return EVENT_HANDLED; + } + // 2 clicks: highest mode (ceiling) + else if (event == EV_2clicks) { + set_state(steady_state, MAX_LEVEL); + return EVENT_HANDLED; + } + // 3 clicks (initial press): off, to prep for later events + else if (event == EV_click3_press) { + set_level(0); + return EVENT_HANDLED; + } + #ifdef USE_BATTCHECK + // 3 clicks: battcheck mode / blinky mode group 1 + else if (event == EV_3clicks) { + set_state(battcheck_state, 0); + return EVENT_HANDLED; + } + #endif + // 4 clicks: momentary + else if (event == EV_4clicks) { + blink_confirm(1); + set_state(momentary_state, 0); + return EVENT_HANDLED; + } + // 6 clicks: lockout mode + else if (event == EV_6clicks) { + blink_confirm(2); + set_state(lockout_state, 0); + return EVENT_HANDLED; + } + #ifdef USE_INDICATOR_LED + // 7 clicks: next aux LED mode + else if (event == EV_7clicks) { + blink_confirm(1); + uint8_t mode = (indicator_led_mode & 3) + 1; + #ifdef TICK_DURING_STANDBY + mode = mode & 3; + #else + mode = mode % 3; + #endif + #ifdef INDICATOR_LED_SKIP_LOW + if (mode == 1) { mode ++; } + #endif + indicator_led_mode = (indicator_led_mode & 0b11111100) | mode; + indicator_led(mode); + save_config(); + return EVENT_HANDLED; + } + #endif + // 8 clicks: beacon mode + else if (event == EV_8clicks) { + set_state(beacon_state, 0); + return EVENT_HANDLED; + } + #ifdef USE_TENCLICK_THERMAL_CONFIG + // 10 clicks: thermal config mode + else if (event == EV_10clicks) { + push_state(thermal_config_state, 0); + return EVENT_HANDLED; + } + #endif + return EVENT_NOT_HANDLED; +} + + +uint8_t steady_state(Event event, uint16_t arg) { + uint8_t mode_min = ramp_smooth_floor; + uint8_t mode_max = ramp_smooth_ceil; + uint8_t ramp_step_size = 1; + #ifdef USE_REVERSING + static int8_t ramp_direction = 1; + #endif + if (ramp_style) { + mode_min = ramp_discrete_floor; + mode_max = ramp_discrete_ceil; + ramp_step_size = ramp_discrete_step_size; + } + + // turn LED on when we first enter the mode + if ((event == EV_enter_state) || (event == EV_reenter_state)) { + // if we just got back from config mode, go back to memorized level + if (event == EV_reenter_state) { + arg = memorized_level; + } + // remember this level, unless it's moon or turbo + if ((arg > mode_min) && (arg < mode_max)) + memorized_level = arg; + // use the requested level even if not memorized + arg = nearest_level(arg); + #ifdef USE_THERMAL_REGULATION + target_level = arg; + #endif + set_level(arg); + #ifdef USE_REVERSING + ramp_direction = 1; + #endif + return EVENT_HANDLED; + } + // 1 click: off + else if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: go to/from highest level + else if (event == EV_2clicks) { + if (actual_level < MAX_LEVEL) { + #ifdef USE_THERMAL_REGULATION + target_level = MAX_LEVEL; + #endif + // true turbo, not the mode-specific ceiling + set_level(MAX_LEVEL); + } + else { + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #endif + set_level(memorized_level); + } + return EVENT_HANDLED; + } + // 3 clicks: toggle smooth vs discrete ramping + else if (event == EV_3clicks) { + ramp_style = !ramp_style; + memorized_level = nearest_level(actual_level); + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #ifdef USE_SET_LEVEL_GRADUALLY + //set_level_gradually(lvl); + #endif + #endif + save_config(); + set_level(0); + delay_4ms(20/4); + set_level(memorized_level); + return EVENT_HANDLED; + } + #ifdef USE_RAMP_CONFIG + // 4 clicks: configure this ramp mode + else if (event == EV_4clicks) { + push_state(ramp_config_state, 0); + return EVENT_HANDLED; + } + #endif + // hold: change brightness (brighter) + else if (event == EV_click1_hold) { + // ramp slower in discrete mode + if (ramp_style && (arg % HOLD_TIMEOUT != 0)) { + return EVENT_HANDLED; + } + #ifdef USE_REVERSING + // make it ramp down instead, if already at max + if ((arg <= 1) && (actual_level >= mode_max)) { + ramp_direction = -1; + } + memorized_level = nearest_level((int16_t)actual_level \ + + (ramp_step_size * ramp_direction)); + #else + memorized_level = nearest_level((int16_t)actual_level + ramp_step_size); + #endif + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #endif + #if defined(BLINK_AT_RAMP_CEILING) || defined(BLINK_AT_RAMP_MIDDLE) + // only blink once for each threshold + if ((memorized_level != actual_level) && ( + 0 // for easier syntax below + #ifdef BLINK_AT_RAMP_MIDDLE_1 + || (memorized_level == BLINK_AT_RAMP_MIDDLE_1) + #endif + #ifdef BLINK_AT_RAMP_MIDDLE_2 + || (memorized_level == BLINK_AT_RAMP_MIDDLE_2) + #endif + #ifdef BLINK_AT_RAMP_CEILING + || (memorized_level == mode_max) + #endif + #if defined(USE_REVERSING) && defined(BLINK_AT_RAMP_FLOOR) + || (memorized_level == mode_min) + #endif + )) { + set_level(0); + delay_4ms(8/4); + } + #endif + #if defined(BLINK_AT_STEPS) + uint8_t foo = ramp_style; + ramp_style = 1; + uint8_t nearest = nearest_level((int16_t)actual_level); + ramp_style = foo; + // only blink once for each threshold + if ((memorized_level != actual_level) && + (ramp_style == 0) && + (memorized_level == nearest) + ) + { + set_level(0); + delay_4ms(8/4); + } + #endif + set_level(memorized_level); + return EVENT_HANDLED; + } + #if defined(USE_REVERSING) + // reverse ramp direction on hold release + else if (event == EV_click1_hold_release) { + #ifdef USE_REVERSING + ramp_direction = -ramp_direction; + #endif + return EVENT_HANDLED; + } + #endif + // click, hold: change brightness (dimmer) + else if (event == EV_click2_hold) { + #ifdef USE_REVERSING + ramp_direction = 1; + #endif + // ramp slower in discrete mode + if (ramp_style && (arg % HOLD_TIMEOUT != 0)) { + return EVENT_HANDLED; + } + // TODO? make it ramp up instead, if already at min? + memorized_level = nearest_level((int16_t)actual_level - ramp_step_size); + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #endif + #if defined(BLINK_AT_RAMP_FLOOR) || defined(BLINK_AT_RAMP_MIDDLE) + // only blink once for each threshold + if ((memorized_level != actual_level) && ( + 0 // for easier syntax below + #ifdef BLINK_AT_RAMP_MIDDLE_1 + || (memorized_level == BLINK_AT_RAMP_MIDDLE_1) + #endif + #ifdef BLINK_AT_RAMP_MIDDLE_2 + || (memorized_level == BLINK_AT_RAMP_MIDDLE_2) + #endif + #ifdef BLINK_AT_RAMP_FLOOR + || (memorized_level == mode_min) + #endif + )) { + set_level(0); + delay_4ms(8/4); + } + #endif + #if defined(BLINK_AT_STEPS) + uint8_t foo = ramp_style; + ramp_style = 1; + uint8_t nearest = nearest_level((int16_t)actual_level); + ramp_style = foo; + // only blink once for each threshold + if ((memorized_level != actual_level) && + (ramp_style == 0) && + (memorized_level == nearest) + ) + { + set_level(0); + delay_4ms(8/4); + } + #endif + set_level(memorized_level); + return EVENT_HANDLED; + } + #if defined(USE_SET_LEVEL_GRADUALLY) || defined(USE_REVERSING) + else if (event == EV_tick) { + #ifdef USE_REVERSING + // un-reverse after 1 second + if (arg == TICKS_PER_SECOND) ramp_direction = 1; + #endif + #ifdef USE_SET_LEVEL_GRADUALLY + // make thermal adjustment speed scale with magnitude + if ((arg & 1) && (actual_level < THERM_FASTER_LEVEL)) { + return EVENT_HANDLED; // adjust slower when not a high mode + } + #ifdef THERM_HARD_TURBO_DROP + else if ((! (actual_level < THERM_FASTER_LEVEL)) + && (actual_level > gradual_target)) { + gradual_tick(); + } + else { + #endif + // [int(62*4 / (x**0.8)) for x in (1,2,4,8,16,32,64,128)] + //uint8_t intervals[] = {248, 142, 81, 46, 26, 15, 8, 5}; + // [int(62*4 / (x**0.9)) for x in (1,2,4,8,16,32,64,128)] + //uint8_t intervals[] = {248, 132, 71, 38, 20, 10, 5, 3}; + // [int(62*4 / (x**0.95)) for x in (1,2,4,8,16,32,64,128)] + uint8_t intervals[] = {248, 128, 66, 34, 17, 9, 4, 2}; + uint8_t diff; + static uint8_t ticks_since_adjust = 0; + ticks_since_adjust ++; + if (gradual_target > actual_level) diff = gradual_target - actual_level; + else { + diff = actual_level - gradual_target; + } + uint8_t magnitude = 0; + #ifndef THERM_HARD_TURBO_DROP + // if we're on a really high mode, drop faster + if (actual_level >= THERM_FASTER_LEVEL) { magnitude ++; } + #endif + while (diff) { + magnitude ++; + diff >>= 1; + } + uint8_t ticks_per_adjust = intervals[magnitude]; + if (ticks_since_adjust > ticks_per_adjust) + { + gradual_tick(); + ticks_since_adjust = 0; + } + //if (!(arg % ticks_per_adjust)) gradual_tick(); + #ifdef THERM_HARD_TURBO_DROP + } + #endif + #endif + return EVENT_HANDLED; + } + #endif + #ifdef USE_THERMAL_REGULATION + // overheating: drop by an amount proportional to how far we are above the ceiling + else if (event == EV_temperature_high) { + #if 0 + uint8_t foo = actual_level; + set_level(0); + delay_4ms(2); + set_level(foo); + #endif + #ifdef THERM_HARD_TURBO_DROP + if (actual_level > THERM_FASTER_LEVEL) { + #ifdef USE_SET_LEVEL_GRADUALLY + set_level_gradually(THERM_FASTER_LEVEL); + #else + set_level(THERM_FASTER_LEVEL); + #endif + target_level = THERM_FASTER_LEVEL; + } else + #endif + if (actual_level > MIN_THERM_STEPDOWN) { + int16_t stepdown = actual_level - arg; + if (stepdown < MIN_THERM_STEPDOWN) stepdown = MIN_THERM_STEPDOWN; + else if (stepdown > MAX_LEVEL) stepdown = MAX_LEVEL; + #ifdef USE_SET_LEVEL_GRADUALLY + set_level_gradually(stepdown); + #else + set_level(stepdown); + #endif + } + return EVENT_HANDLED; + } + // underheating: increase slowly if we're lower than the target + // (proportional to how low we are) + else if (event == EV_temperature_low) { + #if 0 + uint8_t foo = actual_level; + set_level(0); + delay_4ms(2); + set_level(foo); + #endif + if (actual_level < target_level) { + //int16_t stepup = actual_level + (arg>>1); + int16_t stepup = actual_level + arg; + if (stepup > target_level) stepup = target_level; + else if (stepup < MIN_THERM_STEPDOWN) stepup = MIN_THERM_STEPDOWN; + #ifdef USE_SET_LEVEL_GRADUALLY + set_level_gradually(stepup); + #else + set_level(stepup); + #endif + } + return EVENT_HANDLED; + } + #endif + return EVENT_NOT_HANDLED; +} + + +#ifdef USE_BATTCHECK +uint8_t battcheck_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: tempcheck mode + else if (event == EV_2clicks) { + set_state(tempcheck_state, 0); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} +#endif + + +#ifdef USE_THERMAL_REGULATION +uint8_t tempcheck_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 4 clicks: thermal config mode + else if (event == EV_4clicks) { + push_state(thermal_config_state, 0); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} +#endif + + +#ifdef USE_BEACON_MODE +uint8_t beacon_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // TODO: use sleep ticks to measure time between pulses, + // to save power + // 4 clicks: beacon config mode + else if (event == EV_4clicks) { + push_state(beacon_config_state, 0); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} +#endif // #ifdef USE_BEACON_MODE + + +uint8_t lockout_state(Event event, uint16_t arg) { + #ifdef MOON_DURING_LOCKOUT_MODE + // momentary(ish) moon mode during lockout + // button is being held + if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { + #ifdef LOCKOUT_MOON_LOWEST + // Use lowest moon configured + uint8_t lvl = ramp_smooth_floor; + if (ramp_discrete_floor < lvl) lvl = ramp_discrete_floor; + set_level(lvl); + #elif defined(LOCKOUT_MOON_FANCY) + uint8_t levels[] = { ramp_smooth_floor, ramp_discrete_floor }; + if ((event & 0x0f) == 2) { + set_level(levels[ramp_style^1]); + } else { + set_level(levels[ramp_style]); + } + #else + // Use moon from current ramp + set_level(nearest_level(1)); + #endif + } + // button was released + else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { + set_level(0); + } + #endif + + // regular event handling + // conserve power while locked out + // (allow staying awake long enough to exit, but otherwise + // be persistent about going back to sleep every few seconds + // even if the user keeps pressing the button) + #ifdef USE_INDICATOR_LED + if (event == EV_enter_state) { + indicator_led(indicator_led_mode >> 2); + } else + #endif + if (event == EV_tick) { + if (arg > TICKS_PER_SECOND*2) { + go_to_standby = 1; + #ifdef USE_INDICATOR_LED + indicator_led(indicator_led_mode >> 2); + #endif + } + return EVENT_HANDLED; + } + #if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED) + else if (event == EV_sleep_tick) { + if ((indicator_led_mode & 0b00001100) == 0b00001100) { + indicator_blink(arg); + } + return EVENT_HANDLED; + } + #endif + #ifdef USE_INDICATOR_LED + // 3 clicks: rotate through indicator LED modes (lockout mode) + else if (event == EV_3clicks) { + uint8_t mode = indicator_led_mode >> 2; + #ifdef TICK_DURING_STANDBY + mode = (mode + 1) & 3; + #else + mode = (mode + 1) % 3; + #endif + #ifdef INDICATOR_LED_SKIP_LOW + if (mode == 1) { mode ++; } + #endif + indicator_led_mode = (mode << 2) + (indicator_led_mode & 0x03); + indicator_led(mode); + save_config(); + return EVENT_HANDLED; + } + #endif + // 6 clicks: exit + else if (event == EV_6clicks) { + blink_confirm(1); + set_state(off_state, 0); + return EVENT_HANDLED; + } + + return EVENT_NOT_HANDLED; +} + + +uint8_t momentary_state(Event event, uint16_t arg) { + // TODO: momentary strobe here? (for light painting) + + // light up when the button is pressed; go dark otherwise + // button is being held + if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) { + set_level(memorized_level); + return EVENT_HANDLED; + } + // button was released + else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) { + set_level(0); + //go_to_standby = 1; // sleep while light is off + return EVENT_HANDLED; + } + + // Sleep, dammit! (but wait a few seconds first) + // (because standby mode uses such little power that it can interfere + // with exiting via tailcap loosen+tighten unless you leave power + // disconnected for several seconds, so we want to be awake when that + // happens to speed up the process) + else if ((event == EV_tick) && (actual_level == 0)) { + if (arg > TICKS_PER_SECOND*15) { // sleep after 15 seconds + go_to_standby = 1; // sleep while light is off + // TODO: lighted button should use lockout config? + } + return EVENT_HANDLED; + } + + return EVENT_NOT_HANDLED; +} + + +// ask the user for a sequence of numbers, then save them and return to caller +uint8_t config_state_base(Event event, uint16_t arg, + uint8_t num_config_steps, + void (*savefunc)()) { + static uint8_t config_step; + if (event == EV_enter_state) { + config_step = 0; + set_level(0); + return EVENT_HANDLED; + } + // advance forward through config steps + else if (event == EV_tick) { + if (config_step < num_config_steps) { + push_state(number_entry_state, config_step + 1); + } + else { + // TODO: blink out some sort of success pattern + savefunc(); + save_config(); + //set_state(retstate, retval); + pop_state(); + } + return EVENT_HANDLED; + } + // an option was set (return from number_entry_state) + else if (event == EV_reenter_state) { + config_state_values[config_step] = number_entry_value; + config_step ++; + return EVENT_HANDLED; + } + //return EVENT_NOT_HANDLED; + // eat all other events; don't pass any through to parent + return EVENT_HANDLED; +} + +#ifdef USE_RAMP_CONFIG +void ramp_config_save() { + // parse values + uint8_t val; + if (ramp_style) { // discrete / stepped ramp + + val = config_state_values[0]; + if (val) { ramp_discrete_floor = val; } + + val = config_state_values[1]; + if (val) { ramp_discrete_ceil = MAX_LEVEL + 1 - val; } + + val = config_state_values[2]; + if (val) ramp_discrete_steps = val; + + } else { // smooth ramp + + val = config_state_values[0]; + if (val) { ramp_smooth_floor = val; } + + val = config_state_values[1]; + if (val) { ramp_smooth_ceil = MAX_LEVEL + 1 - val; } + + } +} + +uint8_t ramp_config_state(Event event, uint16_t arg) { + uint8_t num_config_steps; + num_config_steps = 2 + ramp_style; + return config_state_base(event, arg, + num_config_steps, ramp_config_save); +} +#endif // #ifdef USE_RAMP_CONFIG + + +#ifdef USE_THERMAL_REGULATION +void thermal_config_save() { + // parse values + uint8_t val; + + // calibrate room temperature + val = config_state_values[0]; + if (val) { + int8_t rawtemp = temperature - therm_cal_offset; + therm_cal_offset = val - rawtemp; + reset_thermal_history = 1; // invalidate all recent temperature data + } + + val = config_state_values[1]; + if (val) { + // set maximum heat limit + therm_ceil = 30 + val - 1; + } + if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL; +} + +uint8_t thermal_config_state(Event event, uint16_t arg) { + return config_state_base(event, arg, + 2, thermal_config_save); +} +#endif // #ifdef USE_THERMAL_REGULATION + + +#ifdef USE_BEACON_MODE +void beacon_config_save() { + // parse values + uint8_t val = config_state_values[0]; + if (val) { + beacon_seconds = val; + } +} + +uint8_t beacon_config_state(Event event, uint16_t arg) { + return config_state_base(event, arg, + 1, beacon_config_save); +} + +inline void beacon_mode_iter() { + // one iteration of main loop() + set_level(memorized_level); + nice_delay_ms(100); + set_level(0); + nice_delay_ms(((beacon_seconds) * 1000) - 100); +} +#endif // #ifdef USE_BEACON_MODE + + +uint8_t number_entry_state(Event event, uint16_t arg) { + static uint8_t value; + static uint8_t blinks_left; + static uint8_t entry_step; + static uint16_t wait_ticks; + if (event == EV_enter_state) { + value = 0; + blinks_left = arg; + entry_step = 0; + wait_ticks = 0; + return EVENT_HANDLED; + } + // advance through the process: + // 0: wait a moment + // 1: blink out the 'arg' value + // 2: wait a moment + // 3: "buzz" while counting clicks + // 4: save and exit + else if (event == EV_tick) { + // wait a moment + if ((entry_step == 0) || (entry_step == 2)) { + if (wait_ticks < TICKS_PER_SECOND/2) + wait_ticks ++; + else { + entry_step ++; + wait_ticks = 0; + } + } + // blink out the option number + else if (entry_step == 1) { + if (blinks_left) { + if ((wait_ticks & 31) == 10) { + set_level(RAMP_SIZE/4); + } + else if ((wait_ticks & 31) == 20) { + set_level(0); + } + else if ((wait_ticks & 31) == 31) { + blinks_left --; + } + wait_ticks ++; + } + else { + entry_step ++; + wait_ticks = 0; + } + } + else if (entry_step == 3) { // buzz while waiting for a number to be entered + wait_ticks ++; + // buzz for N seconds after last event + if ((wait_ticks & 3) == 0) { + set_level(RAMP_SIZE/6); + } + else if ((wait_ticks & 3) == 2) { + set_level(RAMP_SIZE/8); + } + // time out after 3 seconds + if (wait_ticks > TICKS_PER_SECOND*3) { + //number_entry_value = value; + set_level(0); + entry_step ++; + } + } + else if (entry_step == 4) { + number_entry_value = value; + pop_state(); + } + return EVENT_HANDLED; + } + // count clicks + else if (event == EV_click1_release) { + empty_event_sequence(); + if (entry_step == 3) { // only count during the "buzz" + value ++; + wait_ticks = 0; + // flash briefly + set_level(RAMP_SIZE/2); + delay_4ms(8/2); + set_level(0); + } + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + + +// find the ramp level closest to the target, +// using only the levels which are allowed in the current state +uint8_t nearest_level(int16_t target) { + // bounds check + // using int16_t here saves us a bunch of logic elsewhere, + // by allowing us to correct for numbers < 0 or > 255 in one central place + uint8_t mode_min = ramp_smooth_floor; + uint8_t mode_max = ramp_smooth_ceil; + if (ramp_style) { + mode_min = ramp_discrete_floor; + mode_max = ramp_discrete_ceil; + } + if (target < mode_min) return mode_min; + if (target > mode_max) return mode_max; + // the rest isn't relevant for smooth ramping + if (! ramp_style) return target; + + uint8_t ramp_range = ramp_discrete_ceil - ramp_discrete_floor; + ramp_discrete_step_size = ramp_range / (ramp_discrete_steps-1); + uint8_t this_level = ramp_discrete_floor; + + for(uint8_t i=0; i>1)) + return this_level; + } + return this_level; +} + + +void blink_confirm(uint8_t num) { + for (; num>0; num--) { + set_level(MAX_LEVEL/4); + delay_4ms(10/4); + set_level(0); + delay_4ms(100/4); + } +} + + +#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY) +// beacon-like mode for the indicator LED +void indicator_blink(uint8_t arg) { + #define USE_FANCIER_BLINKING_INDICATOR + #ifdef USE_FANCIER_BLINKING_INDICATOR + + // fancy blink, set off/low/high levels here: + uint8_t seq[] = {0, 1, 2, 1, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0}; + indicator_led(seq[arg & 15]); + + #else // basic blink, 1/8th duty cycle + + if (! (arg & 7)) { + indicator_led(2); + } + else { + indicator_led(0); + } + + #endif +} +#endif + + +void load_config() { + if (load_eeprom()) { + ramp_style = eeprom[0]; + #ifdef USE_RAMP_CONFIG + ramp_smooth_floor = eeprom[1]; + ramp_smooth_ceil = eeprom[2]; + ramp_discrete_floor = eeprom[3]; + ramp_discrete_ceil = eeprom[4]; + ramp_discrete_steps = eeprom[5]; + #endif + #ifdef USE_BEACON_MODE + beacon_seconds = eeprom[6]; + #endif + #ifdef USE_THERMAL_REGULATION + therm_ceil = eeprom[EEPROM_BYTES_BASE]; + therm_cal_offset = eeprom[EEPROM_BYTES_BASE+1]; + #endif + #ifdef USE_INDICATOR_LED + indicator_led_mode = eeprom[EEPROM_BYTES_BASE+EEPROM_THERMAL_BYTES]; + #endif + } +} + +void save_config() { + eeprom[0] = ramp_style; + #ifdef USE_RAMP_CONFIG + eeprom[1] = ramp_smooth_floor; + eeprom[2] = ramp_smooth_ceil; + eeprom[3] = ramp_discrete_floor; + eeprom[4] = ramp_discrete_ceil; + eeprom[5] = ramp_discrete_steps; + #endif + #ifdef USE_BEACON_MODE + eeprom[6] = beacon_seconds; + #endif + #ifdef USE_THERMAL_REGULATION + eeprom[EEPROM_BYTES_BASE ] = therm_ceil; + eeprom[EEPROM_BYTES_BASE+1] = therm_cal_offset; + #endif + #ifdef USE_INDICATOR_LED + eeprom[EEPROM_BYTES_BASE+EEPROM_THERMAL_BYTES] = indicator_led_mode; + #endif + + save_eeprom(); +} + +void low_voltage() { + StatePtr state = current_state; + + // TODO: turn off aux LED(s) when power is really low + + if (0) {} // placeholder + + // in normal mode, step down or turn off + else if (state == steady_state) { + if (actual_level > 1) { + uint8_t lvl = (actual_level >> 1) + (actual_level >> 2); + set_level(lvl); + #ifdef USE_THERMAL_REGULATION + target_level = lvl; + #ifdef USE_SET_LEVEL_GRADUALLY + // not needed? + //set_level_gradually(lvl); + #endif + #endif + } + else { + set_state(off_state, 0); + } + } + // all other modes, just turn off when voltage is low + else { + set_state(off_state, 0); + } +} + + +void setup() { + // blink at power-on to let user know power is connected + set_level(RAMP_SIZE/8); + delay_4ms(3); + set_level(0); + + load_config(); + + push_state(off_state, 0); +} + + +void loop() { + + StatePtr state = current_state; + + if (0) {} + + #ifdef USE_BATTCHECK + else if (state == battcheck_state) { + battcheck(); + } + #endif + + #ifdef USE_BEACON_MODE + else if (state == beacon_state) { + beacon_mode_iter(); + } + #endif + + #ifdef USE_THERMAL_REGULATION + // TODO: blink out therm_ceil during thermal_config_state? + else if (state == tempcheck_state) { + blink_num(temperature); + nice_delay_ms(1000); + } + #endif + + #ifdef USE_IDLE_MODE + else { + // doze until next clock tick + idle_mode(); + } + #endif + +} diff --git a/ui/rampingios/rampingiosv3.svg b/ui/rampingios/rampingiosv3.svg new file mode 100644 index 0000000..bc9e6b3 --- /dev/null +++ b/ui/rampingios/rampingiosv3.svg @@ -0,0 +1,4113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + Ramps: + + + ThermalCfg + + + + + BeaconCfg + + + + + + Ramp + Ceil + Floor + + + Turbo + + + + + Mem + Regulated Hybrid -------------- Direct Drive + + + + + + + + + + + + Ramp + + Cfg + + + + + + Actions + 1 Fast Click + Hold + 3 Fast Clicks + Other Action + + + + 2 Fast Clicks + Click, Hold + RampingIOS V3 + + + + 7 Clicks + + + + OFF + + + + OFF + + + + + + + 4 Clicks + 4 Clicks + Click, Click, Hold + + 6 Clicks + + + + Smooth + + + + Ramp Cfg + + 4 Clicks + + + + 4 Clicks + + + + 1. Floor (click N times for level N)2. Ceiling (click N times for 1 + Turbo - N)3. Number of steps (stepped ramp only) + 1. Current temperature (click N times for N deg C)2. Temperature limit (click N times for 30 C + N) + 1. Beacon speed (click N times for N seconds per flash) + Thermal Cfg + Beacon Cfg + + 4 Clicks + + + + + + Stepped + + + + Tactical + + + BattCheck + + Lockout + + TempCheck + Beacon + + ThermalCfg + + + OFF + + + + + + + + + + + (momentary) + 3 Clicks + 4 Clicks + 6 Clicks + 8 Clicks + 10 Clicks + + + Aux LED + mode + next + + + + + + + + lockout LED + mode + next + 4 Clicks + 4 Clicks + 4 Clicks + + + + + diff --git a/ui/werner/Makefile b/ui/werner/Makefile new file mode 100644 index 0000000..21d85f7 --- /dev/null +++ b/ui/werner/Makefile @@ -0,0 +1,7 @@ +all: + ./build-all.sh + +clean: + rm -f cfg-*.h *.hex *~ *.elf *.o + +.phony: clean diff --git a/ui/werner/README b/ui/werner/README new file mode 100644 index 0000000..5fe392d --- /dev/null +++ b/ui/werner/README @@ -0,0 +1,56 @@ +This is a Werner-style interface for dual-switch lights +(e-switch + clicky switch). What that means is: + +While the clicky switch is off: + + - Click the clicky switch: Turn on, at the last-used level. The clicky + switch works as a momentary mode. + + - Click the clicky switch while holding the e-switch: Go into sort of a + utility mode. + +While on, in a normal steady mode: + + - Click the clicky switch: Turn off. + + - Click the e-switch: Brighter. One step per click. + + - Hold the e-switch: Dimmer. Keep holding to go down multiple steps. + +While in standby, in utility mode: + + - Click the e-switch: Turn on. + + - Hold the e-switch: Turn on at lowest level. + + - Double-click the e-switch: Turn on at highest level. + + - Triple-click the e-switch: Battery check mode. + + - Quad-click the e-switch: Ramp config mode. + +While in battery check mode: + + - Click either switch: Turn off. + + - Double-click the e-switch: Go to temperature check mode. + +While in temperature check mode: + + - Click either switch: Turn off. + + - Double-click the e-switch: Go to battery check mode. + + - Quad-click the e-switch: Go to thermal config mode. + +Ramp config mode and thermal config mode work the same as in Anduril or +RampingIOS V3. The options are: + + - Ramp config mode: + 1. Floor level. + 2. Ceiling level. + 3. Number of steps. + + - Thermal config mode: + 1. Calibrate sensor by entering current temperature in C. + 2. Set temperature limit to 30 C + N clicks. diff --git a/ui/werner/build-all.sh b/ui/werner/build-all.sh new file mode 100755 index 0000000..b114101 --- /dev/null +++ b/ui/werner/build-all.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +cp -av ../anduril/cfg*.h . + +UI=werner + +for TARGET in cfg-*.h ; do + NAME=$(echo "$TARGET" | perl -ne '/cfg-(.*).h/ && print "$1\n";') + echo "===== $NAME =====" + ATTINY=$(grep 'ATTINY:' $TARGET | awk '{ print $3 }') + if [ -z "$ATTINY" ]; then ATTINY=85 ; fi + echo ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}" + ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}" + mv -f "$UI".hex "$UI".$NAME.hex +done diff --git a/ui/werner/werner.c b/ui/werner/werner.c new file mode 100644 index 0000000..f3241ee --- /dev/null +++ b/ui/werner/werner.c @@ -0,0 +1,715 @@ +/* + * Werner: Werner-style dual-switch UI for SpaghettiMonster. + * Side click to go up, side hold to go down, tail click for on/off. + * + * Copyright (C) 2018 Selene ToyKeeper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/********* User-configurable options *********/ +// Physical driver type (uncomment one of the following or define it at the gcc command line) +//#define CONFIGFILE cfg-emisar-d4.h + +#define USE_LVP // FIXME: won't build when this option is turned off + +// parameters for this defined below or per-driver +#define USE_THERMAL_REGULATION +#define DEFAULT_THERM_CEIL 45 // try not to get hotter than this + +// battery readout style (pick one) +#define BATTCHECK_VpT +//#define BATTCHECK_8bars // FIXME: breaks build +//#define BATTCHECK_4bars // FIXME: breaks build + +// cut clock speed at very low modes for better efficiency +// (defined here so config files can override it) +#define USE_DYNAMIC_UNDERCLOCKING + +/***** specific settings for known driver types *****/ +#ifdef CONFIGFILE +#include "tk.h" +#include incfile(CONFIGFILE) +#else +#error You need to define CONFIGFILE +#endif + +// thermal properties, if not defined per-driver +#ifndef MIN_THERM_STEPDOWN +#define MIN_THERM_STEPDOWN MAX_1x7135 // lowest value it'll step down to +#endif +#ifndef THERM_FASTER_LEVEL + #ifdef MAX_Nx7135 + #define THERM_FASTER_LEVEL MAX_Nx7135 // throttle back faster when high + #else + #define THERM_FASTER_LEVEL (RAMP_SIZE*4/5) // throttle back faster when high + #endif +#endif +#ifdef USE_THERMAL_REGULATION +#define USE_SET_LEVEL_GRADUALLY // isn't used except for thermal adjustments +#endif + + +/********* Configure SpaghettiMonster *********/ +#define USE_DELAY_ZERO +#define USE_RAMPING +#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file +#define USE_BATTCHECK +#define USE_IDLE_MODE // reduce power use while awake and no tasks are pending + +// auto-detect how many eeprom bytes +#define USE_EEPROM +#ifdef USE_THERMAL_REGULATION +#define EEPROM_BYTES 5 +#else +#define EEPROM_BYTES 3 +#endif +// for mode memory on tail switch +#define USE_EEPROM_WL +#define EEPROM_WL_BYTES 1 + +#include "spaghetti-monster.h" + + +// FSM states +uint8_t off_state(Event event, uint16_t arg); +// simple numeric entry config menu +uint8_t config_state_base(Event event, uint16_t arg, + uint8_t num_config_steps, + void (*savefunc)()); +#define MAX_CONFIG_VALUES 3 +uint8_t config_state_values[MAX_CONFIG_VALUES]; +// ramping mode and its related config mode +uint8_t steady_state(Event event, uint16_t arg); +uint8_t ramp_config_state(Event event, uint16_t arg); +#ifdef USE_BATTCHECK +uint8_t battcheck_state(Event event, uint16_t arg); +#endif +#ifdef USE_THERMAL_REGULATION +uint8_t tempcheck_state(Event event, uint16_t arg); +uint8_t thermal_config_state(Event event, uint16_t arg); +#endif + +// general helper function for config modes +uint8_t number_entry_state(Event event, uint16_t arg); +// return value from number_entry_state() +volatile uint8_t number_entry_value; + +void blink_confirm(uint8_t num); + +// remember stuff even after battery was changed +void load_config(); +void save_config(); +void save_config_wl(); + +// default ramp options if not overridden earlier per-driver +#ifndef RAMP_DISCRETE_FLOOR + #define RAMP_DISCRETE_FLOOR 1 +#endif +#ifndef RAMP_DISCRETE_CEIL + #define RAMP_DISCRETE_CEIL RAMP_SIZE +#endif +#ifndef RAMP_DISCRETE_STEPS + #define RAMP_DISCRETE_STEPS 7 +#endif + +// brightness control +uint8_t memorized_level = MAX_1x7135; +// smooth vs discrete ramping +volatile uint8_t ramp_discrete_floor = RAMP_DISCRETE_FLOOR; +volatile uint8_t ramp_discrete_ceil = RAMP_DISCRETE_CEIL; +volatile uint8_t ramp_discrete_steps = RAMP_DISCRETE_STEPS; +uint8_t ramp_discrete_step_size; // don't set this + +// calculate the nearest ramp level which would be valid at the moment +// (is a no-op for smooth ramp, but limits discrete ramp to only the +// correct levels for the user's config) +uint8_t nearest_level(int16_t target); + +#ifdef USE_THERMAL_REGULATION +// brightness before thermal step-down +uint8_t target_level = 0; +#endif + + +uint8_t off_state(Event event, uint16_t arg) { + // turn emitter off when entering state + if ((event == EV_enter_state) || (event == EV_reenter_state)) { + // let the user know the power is connected + blink_confirm(1); + // but otherwise stay off + set_level(0); + // sleep while off (lower power use) + go_to_standby = 1; + return EVENT_HANDLED; + } + // go back to sleep eventually if we got bumped but didn't leave "off" state + else if (event == EV_tick) { + if (arg > TICKS_PER_SECOND*2) { + go_to_standby = 1; + } + return EVENT_HANDLED; + } + // hold (initially): go to lowest level, but allow abort for regular click + else if (event == EV_click1_press) { + set_level(nearest_level(1)); + return EVENT_HANDLED; + } + // hold: go to lowest level + else if (event == EV_click1_hold) { + // don't start ramping immediately; + // give the user time to release at moon level + if (arg >= HOLD_TIMEOUT) { + set_state(steady_state, 1); + } + return EVENT_HANDLED; + } + // hold, release quickly: go to lowest level + else if (event == EV_click1_hold_release) { + set_state(steady_state, 1); + return EVENT_HANDLED; + } + // 1 click (before timeout): go to memorized level, but allow abort for double click + else if (event == EV_click1_release) { + set_level(nearest_level(memorized_level)); + return EVENT_HANDLED; + } + // 1 click: regular mode + else if (event == EV_1click) { + set_state(steady_state, memorized_level); + return EVENT_HANDLED; + } + // 2 clicks (initial press): off, to prep for later events + else if (event == EV_click2_press) { + set_level(0); + return EVENT_HANDLED; + } + // click, hold: go to highest level (for ramping down) + else if (event == EV_click2_hold) { + set_state(steady_state, MAX_LEVEL); + return EVENT_HANDLED; + } + // 2 clicks: highest mode + else if (event == EV_2clicks) { + set_state(steady_state, nearest_level(MAX_LEVEL)); + return EVENT_HANDLED; + } + #ifdef USE_BATTCHECK + // 3 clicks: battcheck mode / blinky mode group + else if (event == EV_3clicks) { + set_state(battcheck_state, 0); + return EVENT_HANDLED; + } + #endif + // 4 clicks: configure ramp + else if (event == EV_4clicks) { + push_state(ramp_config_state, 0); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + + +uint8_t steady_state(Event event, uint16_t arg) { + uint8_t mode_min = ramp_discrete_floor; + uint8_t mode_max = ramp_discrete_ceil; + uint8_t ramp_step_size = ramp_discrete_step_size; + + // turn LED on when we first enter the mode + if ((event == EV_enter_state) || (event == EV_reenter_state)) { + // if we just got back from config mode, go back to memorized level + if (event == EV_reenter_state) { + arg = memorized_level; + } + // remember this level, unless it's moon or turbo + if ((arg > mode_min) && (arg < mode_max)) + memorized_level = arg; + // use the requested level even if not memorized + #ifdef USE_THERMAL_REGULATION + target_level = arg; + #endif + set_level(nearest_level(arg)); + return EVENT_HANDLED; + } + // click: brighter + else if (event == EV_click1_release) { + memorized_level = nearest_level((int16_t)actual_level + ramp_step_size); + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #endif + set_level(memorized_level); + // make sure next click will respond quickly + empty_event_sequence(); + // remember mode for later + save_config_wl(); + return EVENT_HANDLED; + } + // hold: dimmer + else if (event == EV_click1_hold) { + // ramp slower in discrete mode + if (arg % HOLD_TIMEOUT != 0) { + return EVENT_HANDLED; + } + memorized_level = nearest_level((int16_t)actual_level - ramp_step_size); + #ifdef USE_THERMAL_REGULATION + target_level = memorized_level; + #endif + set_level(memorized_level); + return EVENT_HANDLED; + } + // reverse ramp direction on hold release + else if (event == EV_click1_hold_release) { + save_config_wl(); + return EVENT_HANDLED; + } + #if defined(USE_SET_LEVEL_GRADUALLY) + // gradual thermal regulation + else if (event == EV_tick) { + #ifdef USE_SET_LEVEL_GRADUALLY + // make thermal adjustment speed scale with magnitude + if ((arg & 1) && (actual_level < THERM_FASTER_LEVEL)) { + return EVENT_HANDLED; // adjust slower when not a high mode + } + #ifdef THERM_HARD_TURBO_DROP + else if ((! (actual_level < THERM_FASTER_LEVEL)) + && (actual_level > gradual_target)) { + gradual_tick(); + } + else { + #endif + // [int(62*4 / (x**0.95)) for x in (1,2,4,8,16,32,64,128)] + uint8_t intervals[] = {248, 128, 66, 34, 17, 9, 4, 2}; + uint8_t diff; + static uint8_t ticks_since_adjust = 0; + ticks_since_adjust ++; + if (gradual_target > actual_level) diff = gradual_target - actual_level; + else { + diff = actual_level - gradual_target; + } + uint8_t magnitude = 0; + #ifndef THERM_HARD_TURBO_DROP + // if we're on a really high mode, drop faster + if (actual_level >= THERM_FASTER_LEVEL) { magnitude ++; } + #endif + while (diff) { + magnitude ++; + diff >>= 1; + } + uint8_t ticks_per_adjust = intervals[magnitude]; + if (ticks_since_adjust > ticks_per_adjust) + { + gradual_tick(); + ticks_since_adjust = 0; + } + //if (!(arg % ticks_per_adjust)) gradual_tick(); + #ifdef THERM_HARD_TURBO_DROP + } + #endif + #endif + return EVENT_HANDLED; + } + #endif + #ifdef USE_THERMAL_REGULATION + // overheating: drop by an amount proportional to how far we are above the ceiling + else if (event == EV_temperature_high) { + #ifdef THERM_HARD_TURBO_DROP + if (actual_level > THERM_FASTER_LEVEL) { + #ifdef USE_SET_LEVEL_GRADUALLY + set_level_gradually(THERM_FASTER_LEVEL); + #else + set_level(THERM_FASTER_LEVEL); + #endif + } else + #endif + if (actual_level > MIN_THERM_STEPDOWN) { + int16_t stepdown = actual_level - arg; + if (stepdown < MIN_THERM_STEPDOWN) stepdown = MIN_THERM_STEPDOWN; + else if (stepdown > MAX_LEVEL) stepdown = MAX_LEVEL; + #ifdef USE_SET_LEVEL_GRADUALLY + set_level_gradually(stepdown); + #else + set_level(stepdown); + #endif + } + return EVENT_HANDLED; + } + // underheating: increase slowly if we're lower than the target + // (proportional to how low we are) + else if (event == EV_temperature_low) { + if (actual_level < target_level) { + //int16_t stepup = actual_level + (arg>>1); + int16_t stepup = actual_level + arg; + if (stepup > target_level) stepup = target_level; + else if (stepup < MIN_THERM_STEPDOWN) stepup = MIN_THERM_STEPDOWN; + #ifdef USE_SET_LEVEL_GRADUALLY + set_level_gradually(stepup); + #else + set_level(stepup); + #endif + } + return EVENT_HANDLED; + } + #endif + return EVENT_NOT_HANDLED; +} + + +#ifdef USE_BATTCHECK +uint8_t battcheck_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + #ifdef USE_THERMAL_REGULATION + // 2 clicks: tempcheck mode + else if (event == EV_2clicks) { + blink_confirm(2); + set_state(tempcheck_state, 0); + return EVENT_HANDLED; + } + #endif + return EVENT_NOT_HANDLED; +} +#endif + +#ifdef USE_THERMAL_REGULATION +uint8_t tempcheck_state(Event event, uint16_t arg) { + // 1 click: off + if (event == EV_1click) { + set_state(off_state, 0); + return EVENT_HANDLED; + } + // 2 clicks: battcheck mode + else if (event == EV_2clicks) { + blink_confirm(1); + set_state(battcheck_state, 0); + return EVENT_HANDLED; + } + // 4 clicks: thermal config mode + else if (event == EV_4clicks) { + push_state(thermal_config_state, 0); + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} +#endif + + +// ask the user for a sequence of numbers, then save them and return to caller +uint8_t config_state_base(Event event, uint16_t arg, + uint8_t num_config_steps, + void (*savefunc)()) { + static uint8_t config_step; + if (event == EV_enter_state) { + config_step = 0; + set_level(0); + return EVENT_HANDLED; + } + // advance forward through config steps + else if (event == EV_tick) { + if (config_step < num_config_steps) { + push_state(number_entry_state, config_step + 1); + } + else { + // TODO: blink out some sort of success pattern + savefunc(); + save_config(); + //set_state(retstate, retval); + pop_state(); + } + return EVENT_HANDLED; + } + // an option was set (return from number_entry_state) + else if (event == EV_reenter_state) { + config_state_values[config_step] = number_entry_value; + config_step ++; + return EVENT_HANDLED; + } + //return EVENT_NOT_HANDLED; + // eat all other events; don't pass any through to parent + return EVENT_HANDLED; +} + +void ramp_config_save() { + // parse values + uint8_t val; + + val = config_state_values[0]; + if (val) { ramp_discrete_floor = val; } + + val = config_state_values[1]; + if (val) { ramp_discrete_ceil = MAX_LEVEL + 1 - val; } + + val = config_state_values[2]; + if (val) ramp_discrete_steps = val; +} + +uint8_t ramp_config_state(Event event, uint16_t arg) { + uint8_t num_config_steps; + num_config_steps = 3; + return config_state_base(event, arg, + num_config_steps, ramp_config_save); +} + + +#ifdef USE_THERMAL_REGULATION +void thermal_config_save() { + // parse values + uint8_t val; + + // calibrate room temperature + val = config_state_values[0]; + if (val) { + int8_t rawtemp = temperature - therm_cal_offset; + therm_cal_offset = val - rawtemp; + reset_thermal_history = 1; // invalidate all recent temperature data + } + + val = config_state_values[1]; + if (val) { + // set maximum heat limit + therm_ceil = 30 + val - 1; + } + if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL; +} + +uint8_t thermal_config_state(Event event, uint16_t arg) { + return config_state_base(event, arg, + 2, thermal_config_save); +} +#endif + + +uint8_t number_entry_state(Event event, uint16_t arg) { + static uint8_t value; + static uint8_t blinks_left; + static uint8_t entry_step; + static uint16_t wait_ticks; + if (event == EV_enter_state) { + value = 0; + blinks_left = arg; + entry_step = 0; + wait_ticks = 0; + return EVENT_HANDLED; + } + // advance through the process: + // 0: wait a moment + // 1: blink out the 'arg' value + // 2: wait a moment + // 3: "buzz" while counting clicks + // 4: save and exit + else if (event == EV_tick) { + // wait a moment + if ((entry_step == 0) || (entry_step == 2)) { + if (wait_ticks < TICKS_PER_SECOND/2) + wait_ticks ++; + else { + entry_step ++; + wait_ticks = 0; + } + } + // blink out the option number + else if (entry_step == 1) { + if (blinks_left) { + if ((wait_ticks & 31) == 10) { + set_level(RAMP_SIZE/4); + } + else if ((wait_ticks & 31) == 20) { + set_level(0); + } + else if ((wait_ticks & 31) == 31) { + blinks_left --; + } + wait_ticks ++; + } + else { + entry_step ++; + wait_ticks = 0; + } + } + else if (entry_step == 3) { // buzz while waiting for a number to be entered + wait_ticks ++; + // buzz for N seconds after last event + if ((wait_ticks & 3) == 0) { + set_level(RAMP_SIZE/6); + } + else if ((wait_ticks & 3) == 2) { + set_level(RAMP_SIZE/8); + } + // time out after 3 seconds + if (wait_ticks > TICKS_PER_SECOND*3) { + //number_entry_value = value; + set_level(0); + entry_step ++; + } + } + else if (entry_step == 4) { + number_entry_value = value; + pop_state(); + } + return EVENT_HANDLED; + } + // count clicks + else if (event == EV_click1_release) { + empty_event_sequence(); + if (entry_step == 3) { // only count during the "buzz" + value ++; + wait_ticks = 0; + // flash briefly + set_level(RAMP_SIZE/2); + delay_4ms(8/2); + set_level(0); + } + return EVENT_HANDLED; + } + return EVENT_NOT_HANDLED; +} + + +// find the ramp level closest to the target, +// using only the levels which are allowed in the current state +uint8_t nearest_level(int16_t target) { + // bounds check + // using int16_t here saves us a bunch of logic elsewhere, + // by allowing us to correct for numbers < 0 or > 255 in one central place + uint8_t mode_min = ramp_discrete_floor; + uint8_t mode_max = ramp_discrete_ceil; + if (target < mode_min) return mode_min; + if (target > mode_max) return mode_max; + + uint8_t ramp_range = ramp_discrete_ceil - ramp_discrete_floor; + ramp_discrete_step_size = ramp_range / (ramp_discrete_steps-1); + uint8_t this_level = ramp_discrete_floor; + + for(uint8_t i=0; i>1)) + return this_level; + } + return this_level; +} + + +void blink_confirm(uint8_t num) { + for (; num>0; num--) { + set_level(MAX_LEVEL/4); + delay_4ms(10/4); + set_level(0); + delay_4ms(100/4); + } +} + + +void load_config() { + if (load_eeprom()) { + ramp_discrete_floor = eeprom[0]; + ramp_discrete_ceil = eeprom[1]; + ramp_discrete_steps = eeprom[2]; + #ifdef USE_THERMAL_REGULATION + therm_ceil = eeprom[3]; + therm_cal_offset = eeprom[4]; + #endif + } + if (load_eeprom_wl()) { + memorized_level = eeprom_wl[0]; + } +} + + +void save_config() { + eeprom[0] = ramp_discrete_floor; + eeprom[1] = ramp_discrete_ceil; + eeprom[2] = ramp_discrete_steps; + #ifdef USE_THERMAL_REGULATION + eeprom[3] = therm_ceil; + eeprom[4] = therm_cal_offset; + #endif + + save_eeprom(); +} + + +void save_config_wl() { + eeprom_wl[0] = memorized_level; + save_eeprom_wl(); +} + + +void low_voltage() { + StatePtr state = current_state; + + // in normal mode, step down or turn off + if (state == steady_state) { + if (actual_level > 1) { + uint8_t lvl = (actual_level >> 1) + (actual_level >> 2); + set_level(lvl); + #ifdef USE_THERMAL_REGULATION + target_level = lvl; + #endif + } + else { + set_state(off_state, 0); + } + } + // all other modes, just turn off when voltage is low + else { + set_state(off_state, 0); + } +} + + +void setup() { + // dual switch: e-switch + power clicky + // power clicky acts as a momentary mode + load_config(); + + if (button_is_pressed()) + // hold button to go to moon + push_state(off_state, 0); + else + // otherwise use memory + push_state(steady_state, memorized_level); +} + + +void loop() { + + StatePtr state = current_state; + + if (0) {} + + #ifdef USE_BATTCHECK + else if (state == battcheck_state) { + battcheck(); + } + #endif + #ifdef USE_THERMAL_REGULATION + // TODO: blink out therm_ceil during thermal_config_state + else if (state == tempcheck_state) { + blink_num(temperature); + nice_delay_ms(1000); + } + #endif + + #ifdef USE_IDLE_MODE + else { + // doze until next clock tick + idle_mode(); + } + #endif + +} -- cgit v1.2.3