SHARE
TWEET

X6R Firmware

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