G2A Many GEOs
SHARE
TWEET

X6R Firmware

a guest Sep 22nd, 2015 79 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
Ledger Nano X - The secure hardware wallet
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