pyro1son

Tripledown dual switch mk2

Jun 27th, 2016
59
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* Firmware for Ferrero Rocher driver
  2. * and other attiny13a-based e-switch lights.
  3. *
  4. * Copyright (C) 2015 Selene Scriven
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. *
  19. *
  20. * ATTINY13A Diagram
  21. * ----
  22. * -|1 8|- VCC
  23. * E-switch -|2 7|- Voltage ADC
  24. * Red LED -|3 6|- PWM
  25. * GND -|4 5|- Green LED
  26. * ----
  27. */
  28.  
  29. #define F_CPU 4800000UL
  30.  
  31. // PWM Mode
  32. #define PHASE 0b00000001
  33. #define FAST 0b00000011
  34. // Default only; this firmware sets PWM type per mode below in MODE_PWM
  35. #define PWM_MODE FAST // PWM mode/speed: PHASE (9 kHz) or FAST (18 kHz)
  36. // (FAST has side effects when PWM=0, can't
  37. // shut off light without putting the MCU to sleep)
  38. // (PHASE might make audible whining sounds)
  39. // PFM not supported in this firmware, don't uncomment
  40. //#define USE_PFM // comment out to disable pulse frequency modulation
  41. // (makes bottom few modes ramp smoother)
  42.  
  43. /*
  44. * =========================================================================
  45. * Settings to modify per driver
  46. */
  47.  
  48. #define LOW_TO_HIGH 1 // order in fast-tap mode (long-press will go the opposite direction)
  49. #define BLINK_ON_POWER // blink once when power is received
  50. // (helpful on e-switch lights, annoying on dual-switch lights)
  51. #define VOLTAGE_MON // Comment out to disable all voltage-related functions:
  52. // (including ramp down and eventual shutoff when battery is low)
  53. #define LOWPASS_VOLTAGE // Average the last 4 voltage readings for smoother results
  54. // (comment out to use only one value, saves space)
  55. #define BATTCHECK_ON_LONG // long-press, short-press -> battery check mode
  56. //#define BATTCHECK_ON_SHORT // short-press, long-press -> battery check mode
  57. // // (also short-press quickly from off back to off)
  58.  
  59. // Switch handling
  60. #define LONG_PRESS_DUR 21 // How many WDT ticks until we consider a press a long press
  61. // 32 is roughly .5 s, 21 is roughly 1/3rd second
  62. #define TICKS_PER_RAMP 21 // How many WDT ticks per step in the ramp (lower == faster ramp)
  63.  
  64. // Must be low to high, starting with 0
  65. // (and the lowest values are highly device-dependent)
  66. #define MODES 0,1,3,12,40,125,254
  67. #define ALT_MODES 0,1,3,12,40,125,0 // Must be low to high, and must start with 0, the defines the level for the secondary output. Comment out if no secondary output
  68. #define MODE_PWM 0,PHASE,PHASE,FAST,FAST,FAST,PHASE // Define one per mode above, 0 for phase-correct, 1 for fast-PWM
  69. // (use PHASE for max to avoid a lingering moon-like mode while holding the button, side effect of FAST mode)
  70.  
  71. #define FET_TURBO // 254 in main will turn on FET on pin3
  72. #define TURBO // Comment out to disable - full output with a step down after n number of seconds
  73. // If turbo is enabled, it will be where 255 is listed in the modes above
  74. #define TURBO_TIMEOUT 5625 // How many WTD ticks before before dropping down (.016 sec each)
  75. // 30 = 1875
  76. // 90 = 5625
  77. // 120 = 7500
  78.  
  79. #define ADC_42 184 // the ADC value we expect for 4.20 volts
  80. #define ADC_100 184 // the ADC value for 100% full (4.2V resting)
  81. #define ADC_75 175 // the ADC value for 75% full (4.0V resting)
  82. #define ADC_50 165 // the ADC value for 50% full (3.8V resting)
  83. #define ADC_25 151 // the ADC value for 25% full (3.5V resting)
  84. #define ADC_0 128 // the ADC value for 0% full (3.0V resting)
  85. #define VOLTAGE_FULL 170 // 3.9 V under load
  86. #define VOLTAGE_GREEN 156 // 3.6 V under load
  87. #define VOLTAGE_YELLOW 142 // 3.3 V under load
  88. #define VOLTAGE_RED 128 // 3.0 V under load
  89. #define ADC_LOW 124 // When do we start ramping down (2.9V)
  90. #define ADC_CRIT 114 // When do we shut the light off (2.7V)
  91. // these two are just for testing low-batt behavior w/ a CR123 cell
  92. //#define ADC_LOW 139 // When do we start ramping down
  93. //#define ADC_CRIT 138 // When do we shut the light off
  94. #define ADC_DELAY 188 // Delay in ticks between low-bat rampdowns (188 ~= 3s)
  95. #define OWN_DELAY // replace default _delay_ms() with ours, comment to disable
  96.  
  97.  
  98. /*
  99. * =========================================================================
  100. */
  101.  
  102. #ifdef OWN_DELAY
  103. #include <util/delay_basic.h>
  104. // Having own _delay_ms() saves some bytes AND adds possibility to use variables as input
  105. static void _delay_ms(uint16_t n)
  106. {
  107. while(n-- > 0)
  108. _delay_loop_2(950);
  109. }
  110. #else
  111. #include <util/delay.h>
  112. #endif
  113.  
  114. #include <avr/pgmspace.h>
  115. #include <avr/io.h>
  116. #include <avr/interrupt.h>
  117. #include <avr/wdt.h>
  118. #include <avr/eeprom.h>
  119. #include <avr/sleep.h>
  120. //#include <avr/power.h>
  121.  
  122. #define SWITCH_PIN PB3 // what pin the switch is connected to, which is Star 4
  123. #define PWM_PIN PB1
  124. #define ALT_PWM_PIN PB0
  125. #define VOLTAGE_PIN PB2
  126. #define FET_PIN PB4 // pin 3
  127. //#define GREEN_PIN PB0 // pin 5
  128. #define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
  129. #define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
  130. #define ADC_PRSCL 0x06 // clk/64
  131.  
  132. #define PWM_LVL OCR0B // OCR0B is the output compare register for PB1
  133. #define ALT_PWM_LVL OCR0A // OCR0A is the output compare register for PB0
  134. #ifdef USE_PFM
  135. #define CEIL_LVL OCR0A // OCR0A is the number of "frames" per PWM loop
  136. #endif
  137.  
  138. #define DB_REL_DUR 0b00001111 // time before we consider the switch released after
  139. // each bit of 1 from the right equals 16ms, so 0x0f = 64ms
  140.  
  141. /*
  142. * The actual program
  143. * =========================================================================
  144. */
  145.  
  146. /*
  147. * global variables
  148. */
  149. const uint8_t modes[] = { MODES };
  150. #ifdef ALT_MODES
  151. const uint8_t alt_modes[] = { ALT_MODES };
  152. #endif
  153. const uint8_t mode_pwm[] = { MODE_PWM };
  154. volatile uint8_t mode_idx = 0;
  155. uint8_t press_duration = 0;
  156. uint8_t voltage_readout = 0;
  157. #ifdef LOWPASS_VOLTAGE
  158. uint8_t voltages[] = { VOLTAGE_FULL, VOLTAGE_FULL, VOLTAGE_FULL, VOLTAGE_FULL };
  159. #endif
  160. PROGMEM const uint8_t voltage_blinks[] = {
  161. ADC_0, // 1 blink for 0%-25%
  162. ADC_25, // 2 blinks for 25%-50%
  163. ADC_50, // 3 blinks for 50%-75%
  164. ADC_75, // 4 blinks for 75%-100%
  165. ADC_100, // 5 blinks for >100%
  166. };
  167.  
  168.  
  169. // Debounced switch press value
  170. int is_pressed()
  171. {
  172. // Keep track of last switch values polled
  173. static uint8_t buffer = 0x00;
  174. // Shift over and tack on the latest value, 0 being low for pressed, 1 for pulled-up for released
  175. buffer = (buffer << 1) | ((PINB & (1 << SWITCH_PIN)) == 0);
  176. return (buffer & DB_REL_DUR);
  177. }
  178.  
  179. void next_mode() {
  180. if (++mode_idx >= sizeof(modes)) {
  181. // Wrap around
  182. mode_idx = 0;
  183. }
  184. }
  185.  
  186. void prev_mode() {
  187. if (mode_idx == 0) {
  188. // Wrap around
  189. mode_idx = sizeof(modes) - 1;
  190. } else {
  191. --mode_idx;
  192. }
  193. }
  194.  
  195. inline void PCINT_on() {
  196. // Enable pin change interrupts
  197. GIMSK |= (1 << PCIE);
  198. }
  199.  
  200. inline void PCINT_off() {
  201. // Disable pin change interrupts
  202. GIMSK &= ~(1 << PCIE);
  203. }
  204.  
  205. // Need an interrupt for when pin change is enabled to ONLY wake us from sleep.
  206. // All logic of what to do when we wake up will be handled in the main loop.
  207. EMPTY_INTERRUPT(PCINT0_vect);
  208.  
  209. inline void WDT_on() {
  210. // Setup watchdog timer to only interrupt, not reset, every 16ms.
  211. cli(); // Disable interrupts
  212. wdt_reset(); // Reset the WDT
  213. WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
  214. WDTCR = (1<<WDTIE); // Enable interrupt every 16ms
  215. sei(); // Enable interrupts
  216. }
  217.  
  218. inline void WDT_off()
  219. {
  220. cli(); // Disable interrupts
  221. wdt_reset(); // Reset the WDT
  222. MCUSR &= ~(1<<WDRF); // Clear Watchdog reset flag
  223. WDTCR |= (1<<WDCE) | (1<<WDE); // Start timed sequence
  224. WDTCR = 0x00; // Disable WDT
  225. sei(); // Enable interrupts
  226. }
  227.  
  228. inline void ADC_on() {
  229. ADMUX = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
  230. DIDR0 |= (1 << ADC_DIDR); // disable digital input on ADC pin to reduce power consumption
  231. ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL; // enable, start, prescale
  232. }
  233.  
  234. inline void ADC_off() {
  235. ADCSRA &= ~(1<<7); //ADC off
  236. }
  237.  
  238. void sleep_until_switch_press()
  239. {
  240. // This routine takes up a lot of program memory :(
  241. // Turn the WDT off so it doesn't wake us from sleep
  242. // Will also ensure interrupts are on or we will never wake up
  243. WDT_off();
  244. // Need to reset press duration since a button release wasn't recorded
  245. press_duration = 0;
  246. // Enable a pin change interrupt to wake us up
  247. // However, we have to make sure the switch is released otherwise we will wake when the user releases the switch
  248. while (is_pressed()) {
  249. _delay_ms(16);
  250. }
  251. PCINT_on();
  252. // turn red+green LEDs off
  253. DDRB = (1 << PWM_PIN); // note the lack of red/green pins here
  254. // with this commented out, the LEDs dim instead of turning off entirely
  255. //PORTB &= 0xff ^ ((1 << FET_PIN) | (1 << GREEN_PIN)); // red+green off
  256. // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
  257. //set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  258. // Now go to sleep
  259. sleep_mode();
  260. // Hey, someone must have pressed the switch!!
  261. // Disable pin change interrupt because it's only used to wake us up
  262. PCINT_off();
  263. // Turn the WDT back on to check for switch presses
  264. WDT_on();
  265. // Go back to main program
  266. }
  267.  
  268. // The watchdog timer is called every 16ms
  269. ISR(WDT_vect) {
  270.  
  271. #ifdef TURBO
  272. static uint16_t turbo_ticks = 0;
  273. #endif
  274. static uint8_t ontime_ticks = 0;
  275. uint8_t i = 0;
  276. #ifdef VOLTAGE_MON
  277. static uint8_t lowbatt_cnt = 0;
  278. #ifdef LOWPASS_VOLTAGE
  279. uint16_t voltage = 0;
  280. #else
  281. uint8_t voltage = 0;
  282. #endif
  283. #endif
  284.  
  285. if (mode_idx == 0) {
  286. ontime_ticks = 0;
  287. } else {
  288. if (ontime_ticks < 255) {
  289. ontime_ticks ++;
  290. }
  291. }
  292.  
  293. if (is_pressed()) {
  294. #ifdef TURBO
  295. // Just always reset turbo timer whenever the button is pressed
  296. turbo_ticks = 0;
  297. #endif
  298. #ifdef VOLTAGE_MON
  299. // Same with the ramp down delay
  300. lowbatt_cnt = 0;
  301. #endif
  302.  
  303. if (press_duration < 255) {
  304. press_duration++;
  305. }
  306.  
  307. // Long press (trigger every TICKS_PER_RAMP time slices)
  308. //if (((press_duration%LONG_PRESS_DUR) == (LONG_PRESS_DUR-1))
  309. if ((press_duration == (LONG_PRESS_DUR+1))
  310. && (! voltage_readout)) {
  311. // Long press
  312. #if LOW_TO_HIGH
  313. prev_mode();
  314. #else
  315. next_mode();
  316. #endif
  317. #ifdef BATTCHECK_ON_LONG
  318. // User short-tapped on and immediately long-pressed off
  319. // (this triggers the voltage check mode)
  320. if ((ontime_ticks < (LONG_PRESS_DUR*2)) && (mode_idx == 0)) {
  321. voltage_readout = 4;
  322. }
  323. #endif
  324. }
  325. // let the user keep holding the button to keep cycling through modes
  326. else if (press_duration == LONG_PRESS_DUR+TICKS_PER_RAMP) {
  327. press_duration = LONG_PRESS_DUR;
  328. }
  329. } else {
  330. // Not pressed
  331. if (press_duration > 0 && press_duration < LONG_PRESS_DUR) {
  332. // Short press
  333. #if LOW_TO_HIGH
  334. next_mode();
  335. #else
  336. prev_mode();
  337. #endif
  338. #ifdef BATTCHECK_ON_SHORT
  339. // If the user keeps short-tapping the button from off, reset the
  340. // on-time timer... otherwise, if we've been on for a while, ignore
  341. if (ontime_ticks < (LONG_PRESS_DUR+TICKS_PER_RAMP)) {
  342. ontime_ticks = 1;
  343. // If the user short-tapped all the way through the modes and went
  344. // to "off" again, start the voltage readout mode
  345. // (also happens if they long-press to first mode then
  346. // immediately tap to turn it off again)
  347. if (mode_idx == 0) {
  348. voltage_readout = 4;
  349. }
  350. }
  351. #endif
  352. } else {
  353. #ifdef TURBO
  354. // Only do turbo check when switch isn't pressed
  355. //if (modes[mode_idx] == 255) { // takes more space
  356. if (mode_idx == sizeof(modes)-1) {
  357. turbo_ticks++;
  358. if (turbo_ticks > TURBO_TIMEOUT) {
  359. // Go to the previous mode
  360. prev_mode();
  361. }
  362. }
  363. #endif
  364. // Only do voltage monitoring when the switch isn't pressed
  365. #ifdef VOLTAGE_MON
  366. // See if conversion is done
  367. if (ADCSRA & (1 << ADIF)) {
  368. #ifdef LOWPASS_VOLTAGE
  369. // Get an average of the past few readings
  370. for (i=0;i<3;i++) {
  371. voltages[i] = voltages[i+1];
  372. }
  373. voltages[3] = ADCH;
  374. voltage = (voltages[0]+voltages[1]+voltages[2]+voltages[3]) >> 2;
  375. #else
  376. voltage = ADCH;
  377. #endif
  378. // See if voltage is lower than what we were looking for
  379. if (voltage < ((mode_idx == 1) ? ADC_CRIT : ADC_LOW)) {
  380. ++lowbatt_cnt;
  381. } else {
  382. lowbatt_cnt = 0;
  383. }
  384.  
  385. // allow us to get another voltage reading, not under load
  386. if (voltage_readout > 1) {
  387. PWM_LVL = 0;
  388. voltage_readout --;
  389. } else if (voltage_readout == 1) {
  390. uint8_t blinks = 0;
  391. PWM_LVL = modes[2]; // brief flash at start of measurement
  392. _delay_ms(5);
  393. //voltage = get_voltage();
  394. // turn off and wait one second before showing the value
  395. // (or not, uses extra space)
  396. PWM_LVL = 0;
  397. _delay_ms(1000);
  398.  
  399. // division takes too much flash space
  400. //voltage = (voltage-ADC_LOW) / (((ADC_42 - 15) - ADC_LOW) >> 2);
  401. // a table uses less space than 5 logic clauses
  402. for (i=0; i<sizeof(voltage_blinks); i++) {
  403. if (voltage > pgm_read_byte(voltage_blinks + i)) {
  404. blinks ++;
  405. }
  406. }
  407.  
  408. // blink up to five times to show voltage
  409. // (~0%, ~25%, ~50%, ~75%, ~100%, >100%)
  410. for(i=0; i<blinks; i++) {
  411. PWM_LVL = 12;
  412. _delay_ms(100);
  413. PWM_LVL = 0;
  414. _delay_ms(400);
  415. }
  416. voltage_readout = 0;
  417. }
  418. }
  419.  
  420. // See if it's been low for a while, and maybe step down
  421. if (lowbatt_cnt >= ADC_DELAY) {
  422. prev_mode();
  423. lowbatt_cnt = 0;
  424. }
  425.  
  426. // Make sure conversion is running for next time through
  427. ADCSRA |= (1 << ADSC);
  428. #endif
  429. }
  430. press_duration = 0;
  431. }
  432. }
  433.  
  434. int main(void)
  435. {
  436. // Set all ports to input, and turn pull-up resistors on for the inputs we are using
  437. DDRB = 0x00;
  438. PORTB = (1 << SWITCH_PIN);
  439.  
  440. // Set the switch as an interrupt for when we turn pin change interrupts on
  441. PCMSK = (1 << SWITCH_PIN);
  442.  
  443. // Set PWM pin to output
  444. #ifdef ALT_MODES
  445. DDRB = (1 << PWM_PIN) | (1 << ALT_PWM_PIN);
  446. #else
  447. DDRB = (1 << PWM_PIN);
  448. #endif
  449.  
  450. // Set timer to do PWM for correct output pin and set prescaler timing
  451. // PWM is set per-mode in this firmware
  452. TCCR0A = 0x20 | PWM_MODE; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
  453. #ifdef USE_PFM
  454. // 0x08 is for variable-speed PWM
  455. TCCR0B = 0x08 | 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
  456. #else
  457. TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
  458. #endif
  459.  
  460. // Turn features on or off as needed
  461. #ifdef VOLTAGE_MON
  462. ADC_on();
  463. #else
  464. ADC_off();
  465. #endif
  466. ACSR |= (1<<7); //AC off
  467.  
  468. #ifdef BLINK_ON_POWER
  469. // blink once to let the user know we have power
  470. #ifdef ALT_MODES
  471. TCCR0A = PHASE | 0b10100000; // Use both outputs
  472. #else
  473. TCCR0A = PHASE | 0b00100000; // Only use the normal output
  474. #endif
  475. #ifdef USE_PFM
  476. CEIL_LVL = 255;
  477. #endif
  478. PWM_LVL = 255;
  479. _delay_ms(3);
  480. PWM_LVL = 0;
  481. _delay_ms(1);
  482. #endif
  483.  
  484. // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
  485. set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  486. sleep_until_switch_press();
  487.  
  488. uint8_t last_mode_idx = 0;
  489.  
  490. while(1) {
  491. // We will never leave this loop. The WDT will interrupt to check for switch presses and
  492. // will change the mode if needed. If this loop detects that the mode has changed, run the
  493. // logic for that mode while continuing to check for a mode change.
  494. if (mode_idx != last_mode_idx) {
  495. // The WDT changed the mode.
  496. if (mode_idx > 0) { // TODO: remove this "if" to save space
  497. // No need to change the mode if we are just turning the light off
  498. // Check if the PWM mode is different
  499.  
  500. if (mode_pwm[last_mode_idx] != mode_pwm[mode_idx]) {
  501. #ifdef ALT_MODES
  502. TCCR0A = mode_pwm[mode_idx] | 0b10100000; // Use both outputs
  503. #else
  504. TCCR0A = mode_pwm[mode_idx] | 0b00100000; // Only use the normal output
  505. #endif
  506. }
  507. }
  508. PWM_LVL = modes[mode_idx];
  509. #ifdef ALT_MODES
  510. ALT_PWM_LVL = alt_modes[mode_idx];
  511. #endif
  512. last_mode_idx = mode_idx;
  513. #ifdef ALT_MODES
  514. if (modes[mode_idx] == 0 && alt_modes[mode_idx] == 0) {
  515. #else
  516. if (mode_idx == 0) {
  517. #endif
  518. // Finish executing instructions for PWM level change
  519. // and/or voltage readout mode before shutdown.
  520. do {
  521. _delay_ms(1);
  522. } while (voltage_readout);
  523. // Go to sleep
  524. sleep_until_switch_press();
  525. }
  526.  
  527. if (mode_idx == 254) {
  528. DDRB = (1 << FET_PIN);
  529. PORTB |= (1 << FET_PIN);
  530. } else {
  531. DDRB = (1 << PWM_PIN) | (1 << ALT_PWM_PIN);
  532. #else
  533. DDRB = (1 << PWM_PIN);
  534. #endif
  535. PORTB &= 0xff ^ (1 << FET_PIN); // FET off
  536. }
  537. }
  538. }
  539.  
  540. return 0; // Standard Return Code
  541. }
RAW Paste Data