Advertisement
Guest User

Untitled

a guest
May 29th, 2015
131
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 11.00 KB | None | 0 0
  1. #define F_CPU 4800000UL
  2.  
  3. #define PHASE 0b00000001
  4.  
  5. #define FAST  0b00000011
  6.  
  7. #define BLINK_ON_POWER      
  8.  
  9. #define VOLTAGE_MON        
  10.  
  11. #define MODES           0,4,10,120,255
  12.  
  13. #define MODE_PWM        0,PHASE,FAST,FAST,PHASE
  14.  
  15. #define ADC_LOW         130 // When do we start ramping
  16.  
  17. #define ADC_CRIT        120 // When do we shut the light off
  18.  
  19. #define ADC_DELAY       188 // Delay in ticks between low-bat rampdowns (188 ~= 3s)
  20.  
  21. #define MOM_EXIT_DUR    128 // .16ms each
  22.  
  23. #define FULL_MODE  -1  
  24.  
  25. /*
  26.  
  27.  * =========================================================================
  28.  
  29.  */
  30.  
  31. #include <avr/pgmspace.h>
  32.  
  33. #include <avr/io.h>
  34.  
  35. #include <util/delay.h>
  36.  
  37. #include <avr/interrupt.h>
  38.  
  39. #include <avr/wdt.h>  
  40.  
  41. #include <avr/eeprom.h>
  42.  
  43. #include <avr/sleep.h>
  44.  
  45. //#include <avr/power.h>
  46.  
  47. #define SWITCH_PIN  PB3     // what pin the switch is connected to, which is Star 4
  48.  
  49. #define PWM_PIN     PB1
  50.  
  51. #define VOLTAGE_PIN PB2
  52.  
  53. #define ADC_CHANNEL 0x01    // MUX 01 corresponds with PB2
  54.  
  55. #define ADC_DIDR    ADC1D   // Digital input disable bit corresponding with PB2
  56.  
  57. #define ADC_PRSCL   0x06    // clk/64
  58.  
  59. #define PWM_LVL1     OCR0B   // OCR0B is the output compare register for PB1
  60.  
  61. #define DB_PRES_DUR 0b00000001 // time before we consider the switch pressed (after first realizing it was pressed)
  62.  
  63. #define DB_REL_DUR  0b00001111 // time before we consider the switch released
  64.  
  65.                                // each bit of 1 from the right equals 16ms, so 0x0f = 64ms
  66.  
  67. #define LONG_PRESS_DUR   128 // How many WDT ticks until we consider a press a long press                        // 32 is roughly .5 s
  68.  
  69. #define LOCK_PRESS_DUR   300
  70.  
  71. const uint8_t modes[]     = { MODES    };
  72.  
  73. const uint8_t mode_pwm[] = { MODE_PWM };
  74.  
  75. volatile int8_t mode_idx = 0;
  76.  
  77. volatile uint8_t press_duration = 0;
  78.  
  79. volatile uint8_t low_to_high = 1;
  80.  
  81. volatile uint8_t locked = 0;
  82.  
  83. volatile uint8_t in_momentary = 0;
  84.  
  85. int8_t real_mode_idx = 0;
  86.  
  87. int is_pressed()
  88.  
  89. {
  90.  
  91.     // Keep track of last switch values polled
  92.  
  93.     static uint8_t buffer = 0x00;
  94.  
  95.     // Shift over and tack on the latest value, 0 being low for pressed, 1 for pulled-up for released
  96.  
  97.     buffer = (buffer << 1) | ((PINB & (1 << SWITCH_PIN)) == 0);
  98.  
  99.     return (buffer & DB_REL_DUR);
  100.  
  101. }
  102.  
  103. inline void next_mode() {
  104.  
  105.     if (++mode_idx >= sizeof(modes)) {
  106.  
  107.         // Wrap around
  108.  
  109.         mode_idx = 1;
  110.  
  111.     }  
  112.  
  113. }
  114.  
  115. inline void prev_mode() {
  116.  
  117.     if (mode_idx == 0 ) {
  118.  
  119.         // Wrap around
  120.  
  121.         mode_idx = sizeof(modes) - 1;
  122.  
  123.     } else {
  124.  
  125.         --mode_idx;
  126.  
  127.     }
  128.  
  129. }
  130.  
  131. inline void PCINT_on() {
  132.  
  133.     // Enable pin change interrupts
  134.  
  135.     GIMSK |= (1 << PCIE);
  136.  
  137. }
  138.  
  139. inline void PCINT_off() {
  140.  
  141.     // Disable pin change interrupts
  142.  
  143.     GIMSK &= ~(1 << PCIE);
  144.  
  145. }
  146.  
  147. // Need an interrupt for when pin change is enabled to ONLY wake us from sleep.
  148.  
  149. // All logic of what to do when we wake up will be handled in the main loop.
  150.  
  151. EMPTY_INTERRUPT(PCINT0_vect);
  152.  
  153. inline void WDT_on() {
  154.  
  155.     // Setup watchdog timer to only interrupt, not reset, every 16ms.
  156.  
  157.     cli();                          // Disable interrupts
  158.  
  159.     wdt_reset();                    // Reset the WDT
  160.  
  161.     WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
  162.  
  163.     WDTCR = (1<<WDTIE);               // Enable interrupt every 16ms
  164.  
  165.     sei();                          // Enable interrupts
  166.  
  167. }
  168.  
  169. inline void WDT_off()
  170.  
  171. {
  172.  
  173.     cli();                          // Disable interrupts
  174.  
  175.     wdt_reset();                    // Reset the WDT
  176.  
  177.     MCUSR &= ~(1<<WDRF);          // Clear Watchdog reset flag
  178.  
  179.     WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
  180.  
  181.     WDTCR = 0x00;                   // Disable WDT
  182.  
  183.     sei();                          // Enable interrupts
  184.  
  185. }
  186.  
  187. inline void ADC_on() {
  188.  
  189.     ADMUX  = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
  190.  
  191.     DIDR0 |= (1 << ADC_DIDR);                         // disable digital input on ADC pin to reduce power consumption
  192.  
  193.     ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;   // enable, start, prescale
  194.  
  195. }
  196.  
  197. inline void ADC_off() {
  198.  
  199.     ADCSRA &= ~(1<<7); //ADC off
  200.  
  201. }
  202.  
  203. void sleep_until_switch_press()
  204.  
  205. {
  206.  
  207.     // This routine takes up a lot of program memory Sad
  208.  
  209.     // Turn the WDT off so it doesn't wake us from sleep
  210.  
  211.     // Will also ensure interrupts are on or we will never wake up
  212.  
  213.     WDT_off();
  214.  
  215.     // Need to reset press duration since a button release wasn't recorded
  216.  
  217.     press_duration = 0;
  218.  
  219.     // Enable a pin change interrupt to wake us up
  220.  
  221.     // However, we have to make sure the switch is released otherwise we will wake when the user releases the switch
  222.  
  223.     while (is_pressed()) {
  224.  
  225.         _delay_ms(16);
  226.  
  227.     }
  228.  
  229.     PCINT_on();
  230.  
  231.     // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
  232.  
  233.     //set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  234.  
  235.     // Now go to sleep
  236.  
  237.     sleep_mode();
  238.  
  239.     // Hey, someone must have pressed the switch!!
  240.  
  241.     // Disable pin change interrupt because it's only used to wake us up
  242.  
  243.     PCINT_off();
  244.  
  245.     // Turn the WDT back on to check for switch presses
  246.  
  247.     WDT_on();
  248.  
  249.     // Go back to main program
  250.  
  251. }
  252.  
  253. // The watchdog timer is called every 16ms
  254.  
  255. ISR(WDT_vect) {
  256.  
  257.     static uint8_t  adc_ticks = ADC_DELAY;
  258.  
  259.     static uint8_t  lowbatt_cnt = 0;
  260.  
  261.     if (is_pressed()) {
  262.  
  263.         if (press_duration < 255) {
  264.  
  265.             press_duration++;
  266.  
  267.         }
  268.  
  269.         if (press_duration == LONG_PRESS_DUR) {
  270.  
  271.             // Long press
  272.  
  273.             if (mode_idx == 0 ) {
  274.  
  275.                 // TODO: if we were off, enter strobe mode
  276.  
  277.                 mode_idx = 4;
  278.  
  279.             } else {
  280.  
  281.                 // if we were on, turn off
  282.  
  283.                 mode_idx = 0;
  284.  
  285.             }
  286.  
  287.         }
  288.  
  289.         // Just always reset turbo timer whenever the button is pressed
  290.  
  291.         //turbo_ticks = 0;
  292.  
  293.         // Same with the ramp down delay
  294.  
  295.         adc_ticks = ADC_DELAY;
  296.  
  297.     } else {
  298.  
  299.         if (press_duration == LOCK_PRESS_DUR) {
  300.  
  301.             locked ^= 1;
  302.  
  303.             if (locked)
  304.  
  305.             {
  306.  
  307.                 mode_idx = 0;
  308.  
  309.             }
  310.  
  311.             else
  312.  
  313.             {
  314.  
  315.                 real_mode_idx = mode_idx;
  316.  
  317.             }
  318.  
  319.         }
  320.  
  321.           if (locked) {  // don't do anything if the lock-out is active
  322.  
  323.            press_duration = 0;
  324.  
  325.            return;
  326.  
  327.           }
  328.  
  329.         // Not pressed
  330.  
  331.         if (press_duration > 0 && press_duration < LONG_PRESS_DUR) {
  332.  
  333.             // Short press
  334.  
  335.             if (mode_idx == 4) {
  336.  
  337.                 // short-press from strobe goes to second-highest mode
  338.  
  339.                 mode_idx = sizeof(modes)-4;
  340.  
  341.             }
  342.  
  343.             else { // regular short press
  344.  
  345.                 if (low_to_high) {
  346.  
  347.                     next_mode();
  348.  
  349.  }
  350.  
  351.  }
  352.  
  353.              // Not pressed
  354.  
  355.              if (locked) {  // don't do anything if the lock-out is active
  356.  
  357.                  press_duration = 0;
  358.  
  359.                  return;
  360.  
  361.              }
  362.  
  363.             // Only do voltage monitoring when the switch isn't pressed
  364.  
  365.         #ifdef VOLTAGE_MON
  366.  
  367.             if (adc_ticks > 0 ) {
  368.  
  369.                 --adc_ticks;
  370.  
  371.             }
  372.  
  373.             if (adc_ticks == 0 ) {
  374.  
  375.                 // See if conversion is done
  376.  
  377.                 if (ADCSRA & (1 << ADIF)) {
  378.  
  379.                     // See if voltage is lower than what we were looking for
  380.  
  381.                     if (ADCH < ((mode_idx == 1) ? ADC_CRIT : ADC_LOW)) {
  382.  
  383.                         ++lowbatt_cnt;
  384.  
  385.                     } else {
  386.  
  387.                         lowbatt_cnt = 0;
  388.  
  389.                     }
  390.  
  391.                 }
  392.  
  393.                 // See if it's been low for a while
  394.  
  395.                 if (lowbatt_cnt >= 4) {
  396.  
  397.                     prev_mode();
  398.  
  399.                     lowbatt_cnt = 0;
  400.  
  401.                     // If we reach 0 here, main loop will go into sleep mode
  402.  
  403.                     // Restart the counter to when we step down again
  404.  
  405.                     adc_ticks = ADC_DELAY;
  406.  
  407.                 }
  408.  
  409.                 // Make sure conversion is running for next time through
  410.  
  411.                 ADCSRA |= (1 << ADSC);
  412.  
  413.             }
  414.  
  415.         #endif
  416.  
  417.         }
  418.  
  419.         press_duration = 0;
  420.  
  421.     }
  422.  
  423. }
  424.  
  425.  
  426.  
  427. int main(void)
  428.  
  429. {  
  430.  
  431.     // Set all ports to input, and turn pull-up resistors on for the inputs we are using
  432.  
  433.     DDRB = 0x00;
  434.  
  435.     PORTB = (1 << SWITCH_PIN);
  436.  
  437.     // Set the switch as an interrupt for when we turn pin change interrupts on
  438.  
  439.     PCMSK = (1 << SWITCH_PIN);
  440.  
  441.     // Set PWM pin to output
  442.  
  443.     DDRB = (1 << PWM_PIN);
  444.  
  445.     // Set timer to do PWM for correct output pin and set prescaler timing
  446.  
  447.     TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
  448.  
  449.     TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
  450.  
  451.     // Turn features on or off as needed
  452.  
  453.     #ifdef VOLTAGE_MON
  454.  
  455.     ADC_on();
  456.  
  457.     #else
  458.  
  459.     ADC_off();
  460.  
  461.     #endif
  462.  
  463.     ACSR   |=  (1<<7); //AC off
  464.  
  465. #ifdef BLINK_ON_POWER
  466.  
  467.     // blink once to let the user know we have power
  468.  
  469.     TCCR0A = PHASE | 0b00100000;  // Only use the normal output
  470.  
  471.     PWM_LVL1 = 60;
  472.  
  473.     _delay_ms(3);
  474.  
  475.     PWM_LVL1 = 0;
  476.  
  477.     _delay_ms(1);
  478.  
  479. #endif
  480.  
  481.     // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
  482.  
  483.     set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  484.  
  485.     sleep_until_switch_press();
  486.  
  487.     uint8_t last_mode_idx = 0;
  488.  
  489.     int8_t real_mode_idx = 0;
  490.  
  491.     while(1) {
  492.  
  493.         // We will never leave this loop.  The WDT will interrupt to check for switch presses and
  494.  
  495.         // will change the mode if needed.  If this loop detects that the mode has changed, run the
  496.  
  497.         // logic for that mode while continuing to check for a mode change.
  498.  
  499.         if (mode_idx != last_mode_idx) {
  500.  
  501.             real_mode_idx = mode_idx;
  502.  
  503.             // The WDT changed the mode.
  504.  
  505.             if (mode_idx == FULL_MODE) {
  506.  
  507.                 // strobe should use second-highest mode
  508.  
  509.                 real_mode_idx = sizeof(modes) - 4;
  510.  
  511.             } else if (mode_idx > 0 ) {
  512.  
  513.                 // No need to change the mode if we are just turning the light off
  514.  
  515.                 // Check if the PWM mode is different
  516.  
  517.                 if (mode_pwm[last_mode_idx] != mode_pwm[mode_idx]) {
  518.  
  519.                     TCCR0A = mode_pwm[mode_idx] | 0b10100000;  // Use both outputs      
  520.  
  521.                 }
  522.  
  523.             }
  524.  
  525.             PWM_LVL1     = modes[real_mode_idx];
  526.  
  527.             last_mode_idx = mode_idx;
  528.  
  529.             // Handle strobe mode
  530.  
  531.             if (mode_idx == FULL_MODE) {
  532.  
  533.                 PWM_LVL1=255;
  534.  
  535.                 TCCR0A=PHASE | 0b00100000;
  536.  
  537.             }
  538.  
  539.             if (real_mode_idx == 0 ) {
  540.  
  541.                 _delay_ms(1); // Need this here, maybe instructions for PWM output not getting executed before shutdown?
  542.  
  543.                 // Go to sleep
  544.  
  545.                 sleep_until_switch_press();
  546.  
  547.             }
  548.  
  549.         }
  550.  
  551.     }
  552.  
  553.     return 0; // Standard Return Code
  554.  
  555.     }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement