pyro1son

TripleDown dual switch mk3

Jun 28th, 2016
74
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.             #ifdef FET_TURBO
  357.             if (modes[mode_idx] == 254) {
  358.                 PWM_LVL = 0;
  359.                 ALT_PWM_LVL = 0;
  360.                 DDRB |= (1 << FET_PIN);
  361.                 PORTB |= (1 << FET_PIN);
  362.             } else {
  363.                 PWM_LVL = modes[mode_idx];
  364.                 ALT_PWM_LVL = alt_modes[mode_idx];
  365.                 PORTB &= ~(1 << FET_PIN);
  366.             }
  367.             #endif
  368.             if (mode_idx == sizeof(modes)-1) {
  369.                 turbo_ticks++;
  370.                 if (turbo_ticks > TURBO_TIMEOUT) {
  371.                     // Go to the previous mode
  372.                     prev_mode();
  373.                 }
  374.             }
  375. #endif
  376.             // Only do voltage monitoring when the switch isn't pressed
  377. #ifdef VOLTAGE_MON
  378.             // See if conversion is done
  379.             if (ADCSRA & (1 << ADIF)) {
  380. #ifdef LOWPASS_VOLTAGE
  381.                 // Get an average of the past few readings
  382.                 for (i=0;i<3;i++) {
  383.                     voltages[i] = voltages[i+1];
  384.                 }
  385.                 voltages[3] = ADCH;
  386.                 voltage = (voltages[0]+voltages[1]+voltages[2]+voltages[3]) >> 2;
  387. #else
  388.                 voltage = ADCH;
  389. #endif
  390.                 // See if voltage is lower than what we were looking for
  391.                 if (voltage < ((mode_idx == 1) ? ADC_CRIT : ADC_LOW)) {
  392.                     ++lowbatt_cnt;
  393.                 } else {
  394.                     lowbatt_cnt = 0;
  395.                 }
  396.  
  397.                 // allow us to get another voltage reading, not under load
  398.                 if (voltage_readout > 1) {
  399.                     PWM_LVL = 0;
  400.                     voltage_readout --;
  401.                 } else if (voltage_readout == 1) {
  402.                     uint8_t blinks = 0;
  403.                     PWM_LVL = modes[2];  // brief flash at start of measurement
  404.                     _delay_ms(5);
  405.                     //voltage = get_voltage();
  406.                     // turn off and wait one second before showing the value
  407.                     // (or not, uses extra space)
  408.                     PWM_LVL = 0;
  409.                     _delay_ms(1000);
  410.  
  411.                     // division takes too much flash space
  412.                     //voltage = (voltage-ADC_LOW) / (((ADC_42 - 15) - ADC_LOW) >> 2);
  413.                     // a table uses less space than 5 logic clauses
  414.                     for (i=0; i<sizeof(voltage_blinks); i++) {
  415.                         if (voltage > pgm_read_byte(voltage_blinks + i)) {
  416.                             blinks ++;
  417.                         }
  418.                     }
  419.  
  420.                     // blink up to five times to show voltage
  421.                     // (~0%, ~25%, ~50%, ~75%, ~100%, >100%)
  422.                     for(i=0; i<blinks; i++) {
  423.                         PWM_LVL = 12;
  424.                         _delay_ms(100);
  425.                         PWM_LVL = 0;
  426.                         _delay_ms(400);
  427.                     }
  428.                     voltage_readout = 0;
  429.                 }
  430.             }
  431.  
  432.             // See if it's been low for a while, and maybe step down
  433.             if (lowbatt_cnt >= ADC_DELAY) {
  434.                 prev_mode();
  435.                 lowbatt_cnt = 0;
  436.             }
  437.  
  438.             // Make sure conversion is running for next time through
  439.             ADCSRA |= (1 << ADSC);
  440. #endif
  441.         }
  442.         press_duration = 0;
  443.     }
  444. }
  445.  
  446. int main(void)
  447. {
  448.     // Set all ports to input, and turn pull-up resistors on for the inputs we are using
  449.     DDRB = 0x00;
  450.     PORTB = (1 << SWITCH_PIN);
  451.  
  452.     // Set the switch as an interrupt for when we turn pin change interrupts on
  453.     PCMSK = (1 << SWITCH_PIN);
  454.  
  455.     // Set PWM pin to output
  456.     #ifdef ALT_MODES
  457.     DDRB = (1 << PWM_PIN) | (1 << ALT_PWM_PIN);
  458.     #else
  459.     DDRB = (1 << PWM_PIN);
  460.     #endif
  461.  
  462.     // Set timer to do PWM for correct output pin and set prescaler timing
  463.     // PWM is set per-mode in this firmware
  464.     TCCR0A = 0x20 | PWM_MODE; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
  465.     #ifdef USE_PFM
  466.     // 0x08 is for variable-speed PWM
  467.     TCCR0B = 0x08 | 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
  468.     #else
  469.     TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
  470.     #endif
  471.  
  472.     // Turn features on or off as needed
  473.     #ifdef VOLTAGE_MON
  474.     ADC_on();
  475.     #else
  476.     ADC_off();
  477.     #endif
  478.     ACSR   |=  (1<<7); //AC off
  479.  
  480.     #ifdef BLINK_ON_POWER
  481.     // blink once to let the user know we have power
  482.     #ifdef ALT_MODES
  483.     TCCR0A = PHASE | 0b10100000;  // Use both outputs
  484.     #else
  485.     TCCR0A = PHASE | 0b00100000;  // Only use the normal output
  486.     #endif
  487.     #ifdef USE_PFM
  488.     CEIL_LVL = 255;
  489.     #endif
  490.     PWM_LVL = 255;
  491.     _delay_ms(3);
  492.     PWM_LVL = 0;
  493.     _delay_ms(1);
  494.     #endif
  495.  
  496.     // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
  497.     set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  498.     sleep_until_switch_press();
  499.  
  500.     uint8_t last_mode_idx = 0;
  501.  
  502.     while(1) {
  503.         // We will never leave this loop.  The WDT will interrupt to check for switch presses and
  504.         // will change the mode if needed.  If this loop detects that the mode has changed, run the
  505.         // logic for that mode while continuing to check for a mode change.
  506.         if (mode_idx != last_mode_idx) {
  507.             // The WDT changed the mode.
  508.             if (mode_idx > 0) { // TODO: remove this "if" to save space
  509.                 // No need to change the mode if we are just turning the light off
  510.                 // Check if the PWM mode is different
  511.  
  512.                 if (mode_pwm[last_mode_idx] != mode_pwm[mode_idx]) {
  513.                     #ifdef ALT_MODES
  514.                     TCCR0A = mode_pwm[mode_idx] | 0b10100000;  // Use both outputs
  515.                     #else
  516.                     TCCR0A = mode_pwm[mode_idx] | 0b00100000;  // Only use the normal output
  517.                     #endif
  518.                 }
  519.             }
  520.             PWM_LVL     = modes[mode_idx];
  521.             #ifdef ALT_MODES
  522.             ALT_PWM_LVL = alt_modes[mode_idx];
  523.             #endif
  524.             last_mode_idx = mode_idx;
  525.             #ifdef ALT_MODES
  526.             if (modes[mode_idx] == 0 && alt_modes[mode_idx] == 0) {
  527.             #else
  528.             if (mode_idx == 0) {
  529.             #endif
  530.                 // Finish executing instructions for PWM level change
  531.                 // and/or voltage readout mode before shutdown.
  532.                 do {
  533.                     _delay_ms(1);
  534.                 } while (voltage_readout);
  535.                 // Go to sleep
  536.                 sleep_until_switch_press();
  537.             }
  538.         }
  539.     }
  540.  
  541.     return 0; // Standard Return Code
  542. }
RAW Paste Data