Advertisement
pyro1son

X6R Firmware with batt low indicator LED V1.02

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