fixed_it

STAR_momentary firmware adapted for PD68 TripleDown

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