Advertisement
pyro1son

Tripledown dual switch mk4

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