Advertisement
Jaimelito_

Jaimelito 1 LED

Jun 11th, 2015
622
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 10.53 KB | None | 0 0
  1. #define F_CPU 8000000UL
  2.  
  3. // PWM Mode
  4. #define PHASE 0b00000001
  5. #define FAST  0b00000011
  6.  
  7. #define BLINK_ON_POWER      // blink once when power is received
  8.                             // (helpful on e-switch lights, annoying on dual-switch lights)
  9. #define VOLTAGE_MON         // Comment out to disable - ramp down and eventual shutoff when battery is low
  10. #define MODES           0,4,10,120,255      // Must be low to high, and must start with 0
  11. #define MODE_PWM        0,PHASE,FAST,FAST,PHASE     // Define one per mode above. 0 tells the light to go to sleep
  12.                              
  13. #define ADC_LOW         130 // When do we start ramping
  14. #define ADC_CRIT        120 // When do we shut the light off
  15. #define ADC_DELAY       188 // Delay in ticks between low-bat rampdowns (188 ~= 3s)
  16.  
  17. #define MOM_EXIT_DUR    128 // .16ms each
  18.  
  19. #define FULL_MODE  -1  // A mode of "-1" will be interpreted as Full mode
  20.  
  21. #include <avr/pgmspace.h>
  22. #include <avr/io.h>
  23. #include <util/delay.h>
  24. #include <avr/interrupt.h>
  25. #include <avr/wdt.h>  
  26. #include <avr/eeprom.h>
  27. #include <avr/sleep.h>
  28. //#include <avr/power.h>
  29.  
  30.  
  31. #define SWITCH_PIN  PB3     // what pin the switch is connected to, which is Star 4
  32. #define PWM_PIN     PB1
  33. #define VOLTAGE_PIN PB2
  34. #define ADC_CHANNEL 0x01    // MUX 01 corresponds with PB2
  35. #define ADC_DIDR    ADC1D   // Digital input disable bit corresponding with PB2
  36. #define ADC_PRSCL   0x06    // clk/64
  37.  
  38. #define PWM_LVL1     OCR0B   // OCR0B is the output compare register for PB1
  39.  
  40. #define DB_PRES_DUR 0b00000001 // time before we consider the switch pressed (after first realizing it was pressed)
  41. #define DB_REL_DUR  0b00001111 // time before we consider the switch released
  42.                                // each bit of 1 from the right equals 16ms, so 0x0f = 64ms
  43.  
  44. // Switch handling
  45. #define LONG_PRESS_DUR   128 // How many WDT ticks until we consider a press a long press                      
  46.  
  47. const uint8_t modes[]     = { MODES    };
  48. const uint8_t mode_pwm[] = { MODE_PWM };
  49. volatile int8_t mode_idx = 0;
  50. volatile uint8_t press_duration = 0;
  51. volatile uint8_t low_to_high = 1;
  52. volatile uint8_t in_momentary = 0;
  53.  
  54. // Debounce switch press value
  55.  
  56. int is_pressed()
  57. {
  58.     // Keep track of last switch values polled
  59.     static uint8_t buffer = 0x00;
  60.     // Shift over and tack on the latest value, 0 being low for pressed, 1 for pulled-up for released
  61.     buffer = (buffer << 1) | ((PINB & (1 << SWITCH_PIN)) == 0);
  62.      
  63.     return (buffer & DB_REL_DUR);
  64. }
  65.  
  66.  
  67. inline void next_mode() {
  68.     if (++mode_idx >= sizeof(modes)) {
  69.         // Wrap around
  70.         mode_idx = 1;
  71.     }  
  72. }
  73.  
  74. inline void prev_mode() {
  75.     if (mode_idx == 0) {
  76.         // Wrap around
  77.         mode_idx = sizeof(modes) - 1;
  78.     } else {
  79.         --mode_idx;
  80.     }
  81. }
  82.  
  83. inline void PCINT_on() {
  84.     // Enable pin change interrupts
  85.     GIMSK |= (1 << PCIE);
  86. }
  87.  
  88. inline void PCINT_off() {
  89.     // Disable pin change interrupts
  90.     GIMSK &= ~(1 << PCIE);
  91. }
  92.  
  93. // Need an interrupt for when pin change is enabled to ONLY wake us from sleep.
  94. // All logic of what to do when we wake up will be handled in the main loop.
  95. EMPTY_INTERRUPT(PCINT0_vect);
  96.  
  97. inline void WDT_on() {
  98.     // Setup watchdog timer to only interrupt, not reset, every 16ms.
  99.     cli();                          // Disable interrupts
  100.     wdt_reset();                    // Reset the WDT
  101.     WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
  102.     WDTCR = (1<<WDTIE);               // Enable interrupt every 16ms
  103.     sei();                          // Enable interrupts
  104. }
  105.  
  106. inline void WDT_off()
  107. {
  108.     cli();                          // Disable interrupts
  109.     wdt_reset();                    // Reset the WDT
  110.     MCUSR &= ~(1<<WDRF);          // Clear Watchdog reset flag
  111.     WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
  112.     WDTCR = 0x00;                   // Disable WDT
  113.     sei();                          // Enable interrupts
  114. }
  115.  
  116. inline void ADC_on() {
  117.     ADMUX  = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
  118.     DIDR0 |= (1 << ADC_DIDR);                         // disable digital input on ADC pin to reduce power consumption
  119.     ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;   // enable, start, prescale
  120. }
  121.  
  122. inline void ADC_off() {
  123.     ADCSRA &= ~(1<<7); //ADC off
  124. }
  125.  
  126. void sleep_until_switch_press()
  127. {
  128.     // This routine takes up a lot of program memory :(
  129.     // Turn the WDT off so it doesn't wake us from sleep
  130.     // Will also ensure interrupts are on or we will never wake up
  131.     WDT_off();
  132.     // Need to reset press duration since a button release wasn't recorded
  133.     press_duration = 0;
  134.     // Enable a pin change interrupt to wake us up
  135.     // However, we have to make sure the switch is released otherwise we will wake when the user releases the switch
  136.     while (is_pressed()) {
  137.         _delay_ms(16);
  138.     }
  139.     PCINT_on();
  140.     // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
  141.     //set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  142.     // Now go to sleep
  143.     sleep_mode();
  144.     // Hey, someone must have pressed the switch!!
  145.     // Disable pin change interrupt because it's only used to wake us up
  146.     PCINT_off();
  147.     // Turn the WDT back on to check for switch presses
  148.     WDT_on();
  149.     // Go back to main program
  150. }
  151.  
  152. // The watchdog timer is called every 16ms
  153. ISR(WDT_vect) {
  154.  
  155.     static uint8_t  adc_ticks = ADC_DELAY;
  156.     static uint8_t  lowbatt_cnt = 0;
  157.  
  158.     if (is_pressed()) {
  159.         if (press_duration < 255) {
  160.             press_duration++;
  161.         }
  162.          
  163.          
  164.  
  165.         if (press_duration == LONG_PRESS_DUR) {
  166.             // Long press
  167.             if (mode_idx == 0) {
  168.                 // TODO: if we were off, enter strobe mode
  169.                 mode_idx = 4;
  170.             } else {
  171.                 // if we were on, turn off
  172.                 mode_idx = 0;
  173.             }
  174.         }
  175.          
  176.         // Just always reset turbo timer whenever the button is pressed
  177.         //turbo_ticks = 0;
  178.         // Same with the ramp down delay
  179.         adc_ticks = ADC_DELAY;
  180.      
  181.     } else {
  182.          
  183.          
  184.          
  185.         // Not pressed
  186.         if (press_duration > 0 && press_duration < LONG_PRESS_DUR) {
  187.             // Short press
  188.             if (mode_idx == 4) {
  189.                 // short-press from strobe goes to second-highest mode
  190.                 mode_idx = sizeof(modes)-4;
  191.             }
  192.             else { // regular short press
  193.                 if (low_to_high) {
  194.                     next_mode();
  195.                 } else {
  196.                     prev_mode();
  197.                 }  
  198.             }
  199.         } else {
  200.              
  201.             // Only do voltage monitoring when the switch isn't pressed
  202.         #ifdef VOLTAGE_MON
  203.             if (adc_ticks > 0) {
  204.                 --adc_ticks;
  205.             }
  206.             if (adc_ticks == 0) {
  207.                 // See if conversion is done
  208.                 if (ADCSRA & (1 << ADIF)) {
  209.                     // See if voltage is lower than what we were looking for
  210.                     if (ADCH < ((mode_idx == 1) ? ADC_CRIT : ADC_LOW)) {
  211.                         ++lowbatt_cnt;
  212.                     } else {
  213.                         lowbatt_cnt = 0;
  214.                     }
  215.                 }
  216.                  
  217.                 // See if it's been low for a while
  218.                 if (lowbatt_cnt >= 4) {
  219.                     prev_mode();
  220.                     lowbatt_cnt = 0;
  221.                     // If we reach 0 here, main loop will go into sleep mode
  222.                     // Restart the counter to when we step down again
  223.                     adc_ticks = ADC_DELAY;
  224.                 }
  225.                  
  226.                 // Make sure conversion is running for next time through
  227.                 ADCSRA |= (1 << ADSC);
  228.             }
  229.         #endif
  230.         }
  231.         press_duration = 0;
  232.     }
  233. }
  234.  
  235. int main(void)
  236. {  
  237.     // Set all ports to input, and turn pull-up resistors on for the inputs we are using
  238.     DDRB = 0x00;
  239.     PORTB = (1 << SWITCH_PIN);
  240.  
  241.     // Set the switch as an interrupt for when we turn pin change interrupts on
  242.     PCMSK = (1 << SWITCH_PIN);
  243.      
  244.     // Set PWM pin to output
  245.  
  246.     DDRB = (1 << PWM_PIN);
  247.  
  248.     // Set timer to do PWM for correct output pin and set prescaler timing
  249.     TCCR0A = 0x23; // phase corrected PWM is 0x21 for PB1, fast-PWM is 0x23
  250.     TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
  251.      
  252.     // Turn features on or off as needed
  253.     #ifdef VOLTAGE_MON
  254.     ADC_on();
  255.     #else
  256.     ADC_off();
  257.     #endif
  258.     ACSR   |=  (1<<7); //AC off
  259.      
  260.  
  261.      
  262. #ifdef BLINK_ON_POWER
  263.     // blink once to let the user know we have power
  264.     TCCR0A = PHASE | 0b00100000;  // Only use the normal output
  265.     PWM_LVL1 = 60;
  266.     _delay_ms(3);
  267.     PWM_LVL1 = 0;
  268.     _delay_ms(1);
  269. #endif
  270.  
  271.     // Enable sleep mode set to Power Down that will be triggered by the sleep_mode() command.
  272.     set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  273.     sleep_until_switch_press();
  274.      
  275.     uint8_t last_mode_idx = 0;
  276.     int8_t real_mode_idx = 0;
  277.      
  278.     while(1) {
  279.         // We will never leave this loop.  The WDT will interrupt to check for switch presses and
  280.         // will change the mode if needed.  If this loop detects that the mode has changed, run the
  281.         // logic for that mode while continuing to check for a mode change.
  282.         if (mode_idx != last_mode_idx) {
  283.             real_mode_idx = mode_idx;
  284.             // The WDT changed the mode.
  285.             if (mode_idx == FULL_MODE) {
  286.                 // strobe should use second-highest mode
  287.                 real_mode_idx = sizeof(modes) - 4;
  288.             } else if (mode_idx > 0) {
  289.                 // No need to change the mode if we are just turning the light off
  290.                 // Check if the PWM mode is different
  291.                 if (mode_pwm[last_mode_idx] != mode_pwm[mode_idx]) {
  292.                     TCCR0A = mode_pwm[mode_idx] | 0b10100000;  // Use both outputs      
  293.                 }
  294.             }
  295.             PWM_LVL1     = modes[real_mode_idx];
  296.             last_mode_idx = mode_idx;
  297.             // Handle strobe mode
  298.             if (mode_idx == FULL_MODE) {
  299.                 PWM_LVL1=255;
  300.                 TCCR0A=PHASE | 0b00100000;
  301.              
  302.             }
  303.             if (real_mode_idx == 0) {
  304.                 _delay_ms(1); // Need this here, maybe instructions for PWM output not getting executed before shutdown?
  305.                 // Go to sleep
  306.                 sleep_until_switch_press();
  307.             }
  308.         }
  309.     }
  310.  
  311.     return 0; // Standard Return Code
  312. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement