pyro1son

X6R Firmware V1.2

Oct 8th, 2015
105
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /* PYRO_momentary_charger version 1.2
  2.  * Designed for use with X6R driver designed by pilotdog68, integrating original charger circuit.
  3.  * Changelog
  4.  *
  5.  * 1.0 First stab at this based on MTN_momentary_temp version 1.0
  6.  * 1.1 Addition of Hold to Strobe function 1.1
  7.  * 1.2 ATTINY25 functionality 1.2
  8.  */
  9.  
  10. /*
  11.  * NANJG 105C Diagram
  12.  *                ---
  13.  *              -|   |- VCC
  14.  *       Switch -|   |- Voltage ADC
  15.  *  CHARGER MON -|   |- PWM
  16.  *          GND -|   |- ALT PWM
  17.  *                ---
  18.  *
  19.  * FUSES
  20.  *      I use these fuse settings
  21.  *      Low:  0x75  (4.8MHz CPU without 8x divider, 9.4kHz phase-correct PWM or 18.75kHz fast-PWM)
  22.  *      High: 0xff
  23.  *
  24.  *      For more details on these settings, visit http://github.com/JCapSolutions/blf-firmware/wiki/PWM-Frequency
  25.  *
  26.  * STARS
  27.  *      Star 2 = Alt PWM output
  28.  *      Star 3 = Charger on? input
  29.  *      Star 4 = Switch input
  30.  *
  31.  * VOLTAGE
  32.  *      Resistor values for voltage divider (reference BLF-VLD README for more info)
  33.  *      Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate
  34.  *
  35.  *           VCC
  36.  *            |
  37.  *           Vd (~.25 v drop from protection diode)
  38.  *            |
  39.  *          1912 (R1 19,100 ohms)
  40.  *            |
  41.  *            |---- PB2 from MCU
  42.  *            |
  43.  *          4701 (R2 4,700 ohms)
  44.  *            |
  45.  *           GND
  46.  *
  47.  *      ADC = ((V_bat - V_diode) * R2   * 255) / ((R1    + R2  ) * V_ref)
  48.  *      125 = ((3.0   - .25    ) * 4700 * 255) / ((19100 + 4700) * 1.1  )
  49.  *      121 = ((2.9   - .25    ) * 4700 * 255) / ((19100 + 4700) * 1.1  )
  50.  *
  51.  *      Well 125 and 121 were too close, so it shut off right after lowering to low mode, so I went with
  52.  *      130 and 120
  53.  *
  54.  *      To find out what value to use, plug in the target voltage (V) to this equation
  55.  *          value = (V * 4700 * 255) / (23800 * 1.1)
  56.  *      
  57.  */
  58. // Choose your MCU here, or in the build script
  59. #define ATTINY 13
  60. //#define ATTINY 25
  61.  
  62. // set some hardware-specific values...
  63. // (while configuring this firmware, skip this section)
  64. #if (ATTINY == 13)
  65. #define F_CPU 4800000UL
  66. #define EEPLEN 64
  67. #elif (ATTINY == 25)
  68. #define F_CPU 8000000UL
  69. #define EEPLEN 128
  70. #else
  71. Hey, you need to define ATTINY.
  72. #endif
  73.  
  74. // PWM Mode
  75. #define PHASE 0b00000001
  76. #define FAST  0b00000011
  77.  
  78. //#define OWN_DELAY                                                     // Should we use the built-in delay or our own?
  79.                                                                                                         // Adjust the timing per-driver, since the hardware has high variance
  80.                                                                                                         // Higher values will run slower, lower values run faster.
  81. #if (ATTINY == 13)
  82. #define DELAY_TWEAK         950
  83. #elif (ATTINY == 25)
  84. #define DELAY_TWEAK         2000
  85. #endif
  86. /*
  87.  * =========================================================================
  88.  * Settings to modify per driver
  89.  */
  90.  
  91. #define VOLTAGE_MON         // Comment out to disable - ramp down and eventual shutoff when battery is low
  92. #define CHARGER_MON            // Comment out to disable - is charging cable connected
  93. #define MODES           0,0,0,17,100,255        // Must be low to high, and must start with 0
  94. #define ALT_MODES       0,3,17,0,0,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
  95. #define MODE_PWM        0,PHASE,FAST,FAST,FAST,FAST     // Define one per mode above. 0 tells the light to go to sleep
  96. #define TURBO               // Comment out to disable - full output with a step down after n number of seconds
  97.                             // If turbo is enabled, it will be where 255 is listed in the modes above
  98. #define TURBO_TIMEOUT   5625 // How many WTD ticks before before dropping down (.016 sec each)
  99.                             // 90  = 5625
  100.                             // 120 = 7500
  101.                            
  102. #define BATT_LOW        140 // When do we start ramping
  103. #define BATT_CRIT       130 // When do we shut the light off
  104. #define CHARGER_ON       250 // When we should lower the output
  105. #define BATT_ADC_DELAY  188 // Delay in ticks between low-bat rampdowns (188 ~= 3s)
  106. #define CHARGER_ADC_DELAY   15  // Delay in ticks before checking for charging cable (188 ~= 3s)
  107.  
  108.  
  109. /*
  110.  * =========================================================================
  111.  */
  112.  
  113. #include <avr/pgmspace.h>
  114. #include <avr/io.h>
  115. #ifdef OWN_DELAY
  116. #include <util/delay_basic.h>
  117. // Having own _delay_ms() saves some bytes AND adds possibility to use variables as input
  118. void _delay_ms(uint16_t n)
  119. {
  120.     // TODO: make this take tenths of a ms instead of ms,
  121.     // for more precise timing?
  122.     while(n-- > 0) _delay_loop_2(DELAY_TWEAK);
  123. }
  124. void _delay_s()  // because it saves a bit of ROM space to do it this way
  125. {
  126.     _delay_ms(1000);
  127. }
  128. #else
  129. #include <util/delay.h>
  130. #endif
  131. #include <avr/interrupt.h>
  132. #include <avr/wdt.h>   
  133. #include <avr/eeprom.h>
  134. #include <avr/sleep.h>
  135. //#include <avr/power.h>
  136.  
  137. #define SWITCH_PIN  PB3     // what pin the switch is connected to, which is Star 4
  138. #define PWM_PIN     PB1
  139. #define ALT_PWM_PIN PB0
  140. #define ADC_DIDR    ADC1D   // Digital input disable bit corresponding with PB2
  141. #define ADC_PRSCL   0x06    // clk/64
  142.  
  143. #define PWM_LVL     OCR0B   // OCR0B is the output compare register for PB1
  144. #define ALT_PWM_LVL OCR0A   // OCR0A is the output compare register for PB0
  145.  
  146. //#define DEBOUNCE_BOTH          // Comment out if you don't want to debounce the PRESS along with the RELEASE
  147.                                // PRESS debounce is only needed in special cases where the switch can experience errant signals
  148. #define DB_PRES_DUR 0b00000001 // time before we consider the switch pressed (after first realizing it was pressed)
  149. #define DB_REL_DUR  0b00001111 // time before we consider the switch released
  150.                                // each bit of 1 from the right equals 16ms, so 0x0f = 64ms
  151.  
  152. // Switch handling
  153. #define LONG_PRESS_DUR   32 // How many WDT ticks until we consider a press a long press
  154.                             // 32 is roughly .5 s
  155. #define STROBE_DUR       96 // How many WDT ticks until we enter the hold-to-strobe mode
  156.  
  157.  
  158. /*
  159.  * The actual program
  160.  * =========================================================================
  161.  */
  162.  
  163. /*
  164.  * global variables
  165.  */
  166. const uint8_t modes[]     = { MODES    };
  167. #ifdef ALT_MODES
  168. const uint8_t alt_modes[] = { ALT_MODES };
  169. #endif
  170. const uint8_t mode_pwm[] = { MODE_PWM };
  171. volatile uint8_t mode_idx = 0;
  172. volatile uint8_t press_duration = 0;
  173. volatile uint8_t in_momentary = 0;
  174. #ifdef VOLTAGE_MON
  175. volatile uint8_t adc_channel = 1;   // MUX 01 corresponds with PB2, 02 for PB4. Will switch back and forth
  176. #else
  177. volatile uint8_t adc_channel = 2;   // MUX 01 corresponds with PB2, 02 for PB4. Will switch back and forth
  178. #endif
  179.  
  180. // Debounce switch press value
  181. #ifdef DEBOUNCE_BOTH
  182. int is_pressed()
  183. {
  184.     static uint8_t pressed = 0;
  185.     // Keep track of last switch values polled
  186.     static uint8_t buffer = 0x00;
  187.     // Shift over and tack on the latest value, 0 being low for pressed, 1 for pulled-up for released
  188.     buffer = (buffer << 1) | ((PINB & (1 << SWITCH_PIN)) == 0);
  189.    
  190.     if (pressed) {
  191.         // Need to look for a release indicator by seeing if the last switch status has been 0 for n number of polls
  192.         pressed = (buffer & DB_REL_DUR);
  193.     } else {
  194.         // Need to look for pressed indicator by seeing if the last switch status was 1 for n number of polls
  195.         pressed = ((buffer & DB_PRES_DUR) == DB_PRES_DUR);
  196.     }
  197.  
  198.     return pressed;
  199. }
  200. #else
  201. int is_pressed()
  202. {
  203.     // Keep track of last switch values polled
  204.     static uint8_t buffer = 0x00;
  205.     // Shift over and tack on the latest value, 0 being low for pressed, 1 for pulled-up for released
  206.     buffer = (buffer << 1) | ((PINB & (1 << SWITCH_PIN)) == 0);
  207.    
  208.     return (buffer & DB_REL_DUR);
  209. }
  210. #endif
  211.  
  212. void next_mode() {
  213.     if (++mode_idx >= sizeof(modes)) {
  214.         // Wrap around
  215.         mode_idx = 2;
  216.     }  
  217. }
  218.  
  219. void prev_mode() {
  220.     if (mode_idx == 0) {
  221.     } else {
  222.         --mode_idx;
  223.     }
  224. }
  225.  
  226. inline void PCINT_on() {
  227.     // Enable pin change interrupts
  228.     GIMSK |= (1 << PCIE);
  229. }
  230.  
  231. inline void PCINT_off() {
  232.     // Disable pin change interrupts
  233.     GIMSK &= ~(1 << PCIE);
  234. }
  235.  
  236. // Need an interrupt for when pin change is enabled to ONLY wake us from sleep.
  237. // All logic of what to do when we wake up will be handled in the main loop.
  238. EMPTY_INTERRUPT(PCINT0_vect);
  239.  
  240. inline void WDT_on() {
  241.     // Setup watchdog timer to only interrupt, not reset, every 16ms.
  242.     cli();                          // Disable interrupts
  243.     wdt_reset();                    // Reset the WDT
  244.     WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
  245.     WDTCR = (1<<WDTIE);             // Enable interrupt every 16ms
  246.     sei();                          // Enable interrupts
  247. }
  248.  
  249. inline void WDT_off()
  250. {
  251.     cli();                          // Disable interrupts
  252.     wdt_reset();                    // Reset the WDT
  253.     MCUSR &= ~(1<<WDRF);            // Clear Watchdog reset flag
  254.     WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
  255.     WDTCR = 0x00;                   // Disable WDT
  256.     sei();                          // Enable interrupts
  257. }
  258.  
  259. void ADC_on() {
  260.     ADMUX  = (1 << REFS0) | (1 << ADLAR) | adc_channel; // 1.1v reference, left-adjust, ADC1/PB2 or ADC2/PB3
  261.     DIDR0 |= (1 << ADC_DIDR);                           // disable digital input on ADC pin to reduce power consumption
  262.     ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;   // enable, start, prescale, Single Conversion mode
  263. }
  264.  
  265. void ADC_off() {
  266.     ADCSRA &= ~(1 << ADSC); //ADC off
  267. }
  268.  
  269. void sleep_until_switch_press()
  270. {
  271.     // This routine takes up a lot of program memory :(
  272.     // Turn the WDT off so it doesn't wake us from sleep
  273.     // Will also ensure interrupts are on or we will never wake up
  274.     WDT_off();
  275.     // Need to reset press duration since a button release wasn't recorded
  276.     press_duration = 0;
  277.     // Enable a pin change interrupt to wake us up
  278.     // However, we have to make sure the switch is released otherwise we will wake when the user releases the switch
  279.     while (is_pressed()) {
  280.         _delay_ms(16);
  281.     }
  282.     PCINT_on();
  283.     // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
  284.     //set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  285.     // Now go to sleep
  286.     sleep_mode();
  287.     // Hey, someone must have pressed the switch!!
  288.     // Disable pin change interrupt because it's only used to wake us up
  289.     PCINT_off();
  290.     // Turn the WDT back on to check for switch presses
  291.     WDT_on();
  292.     // Go back to main program
  293. }
  294.  
  295. // The watchdog timer is called every 16ms
  296. ISR(WDT_vect) {
  297.  
  298.     //static uint8_t  press_duration = 0;  // Pressed or not pressed
  299.     static uint16_t turbo_ticks = 0;
  300.     static uint16_t batt_adc_ticks = BATT_ADC_DELAY;
  301.     static uint16_t charger_adc_ticks = CHARGER_ADC_DELAY;
  302.     static uint8_t  lowbatt_cnt = 0;
  303.     static uint8_t  highest_mode_idx = 255;
  304.  
  305.     if (is_pressed()) {
  306.         if (press_duration < 255) {
  307.             press_duration++;
  308.         }
  309.        
  310.         if (press_duration == LONG_PRESS_DUR) {
  311.             // Long press
  312.                 prev_mode();
  313.             highest_mode_idx = mode_idx;
  314.             }
  315.         //hold-to-strobe code
  316.         if (press_duration == STROBE_DUR) {
  317.             while(is_pressed()){
  318.                             //Exit loop once switch is let go
  319.                 PWM_LVL = 255;
  320.                 _delay_ms(20);
  321.                 PWM_LVL=0;
  322.                 _delay_ms(60);
  323.                                 //Can alter values to change strobe duration
  324.             }
  325.         next_mode();
  326.                 //Return to the previous mode because we actually went back a
  327.                 //mode before entering strobe
  328.         }
  329.         // Just always reset turbo timer whenever the button is pressed
  330.         turbo_ticks = 0;
  331.         // Same with the ramp down delay
  332.         batt_adc_ticks = BATT_ADC_DELAY;
  333.         charger_adc_ticks = CHARGER_ADC_DELAY;
  334.    
  335.     } else {
  336.        
  337.         #ifdef MOM_ENTER_DUR
  338.         if (in_momentary) {
  339.             // Turn off the light
  340.             mode_idx = 0;
  341.             return;
  342.         }
  343.         #endif
  344.        
  345.         // Not pressed
  346.         if (press_duration > 0 && press_duration < LONG_PRESS_DUR) {
  347.             // Short press
  348.                 next_mode();
  349.             highest_mode_idx = mode_idx;
  350.         } else {
  351.             // Only do turbo check when switch isn't pressed
  352.         #ifdef TURBO
  353.             if (modes[mode_idx] == 255) {
  354.                 turbo_ticks++;
  355.                 if (turbo_ticks > TURBO_TIMEOUT) {
  356.                     // Go to the previous mode
  357.                     prev_mode();
  358.                 }
  359.             }
  360.         #endif
  361.             // Only do voltage monitoring when the switch isn't pressed
  362.             // See if conversion is done. We moved this up here because we want to stay on
  363.             // the current ADC input until the conversion is done, and then switch to the new
  364.             // input, start the monitoring
  365.             if (batt_adc_ticks > 0) {
  366.                 --batt_adc_ticks;
  367.             }
  368.             if (charger_adc_ticks > 0) {
  369.                 --charger_adc_ticks;
  370.             }
  371.             if (ADCSRA & (1 << ADIF)) {
  372.                 if (adc_channel == 0x01) {
  373.                    
  374.                     if (batt_adc_ticks == 0) {
  375.                         // See if voltage is lower than what we were looking for
  376.                         if (ADCH < ((mode_idx == 1) ? BATT_CRIT : BATT_LOW)) {
  377.                             ++lowbatt_cnt;
  378.                         } else {
  379.                             lowbatt_cnt = 0;
  380.                         }
  381.                    
  382.                         // See if it's been low for a while
  383.                         if (lowbatt_cnt >= 5) {
  384.                             prev_mode();
  385.                             highest_mode_idx = mode_idx;
  386.                             lowbatt_cnt = 0;
  387.                             // If we reach 0 here, main loop will go into sleep mode
  388.                             // Restart the counter to when we step down again
  389.                             batt_adc_ticks = BATT_ADC_DELAY;
  390.                             charger_adc_ticks = CHARGER_ADC_DELAY;
  391.                         }
  392.                     }
  393.                     // Switch ADC to charger monitoring
  394.                     adc_channel = 0x02;
  395.                     ADMUX = ((ADMUX & 0b11111100) | adc_channel);
  396.                 } else if (adc_channel == 0x02) {
  397.                    
  398.                     if (charger_adc_ticks == 0) {
  399.                         // See if charger is higher than the high threshold
  400.                         if (ADCH > ((mode_idx == 1) ? 255 : CHARGER_ON)) {
  401.                             mode_idx = 0;
  402.                             charger_adc_ticks = CHARGER_ADC_DELAY;
  403.                             batt_adc_ticks = BATT_ADC_DELAY;
  404.                         } else {
  405.                             mode_idx;
  406.                             charger_adc_ticks = CHARGER_ADC_DELAY;
  407.                             batt_adc_ticks = BATT_ADC_DELAY;
  408.                         }
  409.                     }
  410.                     #ifdef VOLTAGE_MON
  411.                     // Switch ADC to battery monitoring
  412.                     adc_channel = 0x01;
  413.                     ADMUX = ((ADMUX & 0b11111100) | adc_channel);
  414.                     #endif;
  415.                 }
  416.             }
  417.             // Start conversion for next time through
  418.             ADCSRA |= (1 << ADSC);
  419.         }
  420.         press_duration = 0;
  421.     }
  422. }
  423.  
  424. int main(void)
  425. {  
  426.     // Set all ports to input, and turn pull-up resistors on for the inputs we are using
  427.     DDRB = 0x00;
  428.     PORTB = (1 << SWITCH_PIN);
  429.  
  430.     // Set the switch as an interrupt for when we turn pin change interrupts on
  431.     PCMSK = (1 << SWITCH_PIN);
  432.    
  433.     // Set PWM pin to output
  434.     #ifdef ALT_MODES
  435.     DDRB = (1 << PWM_PIN) | (1 << ALT_PWM_PIN);
  436.     #else
  437.     DDRB = (1 << PWM_PIN);
  438.     #endif
  439.  
  440.     // Set timer to do PWM for correct output pin and set prescaler timing
  441.     //TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
  442.     TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
  443.    
  444.     // Turn features on or off as needed
  445.     ADC_on();
  446.     ACSR   |=  (1<<7); //AC off
  447.    
  448.     // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
  449.     set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  450.     sleep_until_switch_press();
  451.    
  452.     uint8_t last_mode_idx = 0;
  453.    
  454.     while(1) {
  455.         // We will never leave this loop.  The WDT will interrupt to check for switch presses and
  456.         // will change the mode if needed.  If this loop detects that the mode has changed, run the
  457.         // logic for that mode while continuing to check for a mode change.
  458.         if (mode_idx != last_mode_idx) {
  459.             // The WDT changed the mode.
  460.             if (mode_idx > 0) {
  461.                 // No need to change the mode if we are just turning the light off
  462.                 // Check if the PWM mode is different
  463.                 if (mode_pwm[last_mode_idx] != mode_pwm[mode_idx]) {
  464.                     #ifdef ALT_MODES
  465.                     TCCR0A = mode_pwm[mode_idx] | 0b10100000;  // Use both outputs
  466.                     #else
  467.                     TCCR0A = mode_pwm[mode_idx] | 0b00100000;  // Only use the normal output
  468.                     #endif
  469.                 }
  470.             }
  471.             PWM_LVL     = modes[mode_idx];
  472.             #ifdef ALT_MODES
  473.             ALT_PWM_LVL = alt_modes[mode_idx];
  474.             #endif
  475.             last_mode_idx = mode_idx;
  476.             if (mode_pwm[mode_idx] == 0) {
  477.                 _delay_ms(1); // Need this here, maybe instructions for PWM output not getting executed before shutdown?
  478.                 // Go to sleep
  479.                 sleep_until_switch_press();
  480.             }
  481.         }
  482.     }
  483.  
  484.     return 0; // Standard Return Code
  485. }
RAW Paste Data