Guest User

X6R Firmware Lightweight

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

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×