Advertisement
Guest User

Convoy S3 flashlight code

a guest
Apr 1st, 2015
123
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 12.95 KB | None | 0 0
  1. /* STAR version 1.2 - MODIFIED
  2. // Made for Convoy S3 flashlight.
  3. // Has 3 modes (low, medium and maximum) which can be changed below.
  4. // No stars. No memory. 3 seconds for saving mode. Always starts up in lowest mode.
  5. // To use medium, click 2 times within 3 seconds. To use the max mode, click the button 3 times within the same time period (3 seconds).
  6. // Light has a fade effect when turning on a mode. Its not possible to fade out.
  7. // Low voltage code was not tested after the changes.
  8.  
  9. // Ticks are every 32 ms
  10. // Lowest mode for Convoy S3 is: 15 (but flickers slightly). Next mode 16 is brighter and does not flicker
  11.  
  12.  
  13.  *
  14.  * Change log
  15.  *
  16.  * 1.0 Initial version
  17.  * 1.1 Cleaned up the code
  18.  * 1.2 Added support for dual PWM outputs and selection of PWM mode per output level
  19.  * 1.3 Added ability to have turbo ramp down gradually instead of step down
  20.  *
  21.  */
  22.  
  23. /*
  24.  * NANJG 105C Diagram
  25.  *           ---
  26.  *         -|   |- VCC
  27.  *  Star 4 -|   |- Voltage ADC
  28.  *  Star 3 -|   |- PWM
  29.  *     GND -|   |- Star 2
  30.  *           ---
  31.  *
  32.  * FUSES
  33.  *      I use these fuse settings
  34.  *      Low:  0x75  (4.8MHz CPU without 8x divider, 9.4kHz phase-correct PWM or 18.75kHz fast-PWM)
  35.  *      High: 0xff
  36.  *
  37.  *      For more details on these settings, visit http://github.com/JCapSolutions/blf-firmware/wiki/PWM-Frequency
  38.  *
  39.  * STARS
  40.  *      Star 2 = Moon if connected and alternate PWM output not used
  41.  *      Star 3 = H-L if connected, L-H if not
  42.  *      Star 4 = Memory if not connected
  43.  *
  44.  * VOLTAGE
  45.  *      Resistor values for voltage divider (reference BLF-VLD README for more info)
  46.  *      Reference voltage can be anywhere from 1.0 to 1.2, so this cannot be all that accurate
  47.  *
  48.  *           VCC
  49.  *            |
  50.  *           Vd (~.25 v drop from protection diode)
  51.  *            |
  52.  *          1912 (R1 19,100 ohms)
  53.  *            |
  54.  *            |---- PB2 from MCU
  55.  *            |
  56.  *          4701 (R2 4,700 ohms)
  57.  *            |
  58.  *           GND
  59.  *
  60.  *      ADC = ((V_bat - V_diode) * R2   * 255) / ((R1    + R2  ) * V_ref)
  61.  *      125 = ((3.0   - .25    ) * 4700 * 255) / ((19100 + 4700) * 1.1  )
  62.  *      121 = ((2.9   - .25    ) * 4700 * 255) / ((19100 + 4700) * 1.1  )
  63.  *
  64.  *      Well 125 and 121 were too close, so it shut off right after lowering to low mode, so I went with
  65.  *      130 and 120
  66.  *
  67.  *      To find out what value to use, plug in the target voltage (V) to this equation
  68.  *          value = (V * 4700 * 255) / (23800 * 1.1)
  69.  *      
  70.  */
  71. #define F_CPU 4800000UL
  72.  
  73. /*
  74.  * =========================================================================
  75.  * Settings to modify per driver
  76.  */
  77.  
  78. #define VOLTAGE_MON         // Comment out to disable
  79.  
  80. //#define TICKS_250MS       // If enabled, ticks are every 250 ms. If disabled, ticks are every 500 ms
  81.                             // Affects mode saving and turbo timeout/rampdown timing
  82.  
  83. //#define MODE_MOON         15  // Can comment out to remove mode, but should be set through soldering stars
  84. #define MODE_LOW            45  // Can comment out to remove mode
  85. #define MODE_MED            120 // Can comment out to remove mode
  86. //#define MODE_HIGH         16  // Can comment out to remove mode
  87. #define MODE_TURBO          255 // Can comment out to remove mode
  88. #define MODE_TURBO_LOW      140 // Level turbo ramps down to if turbo enabled
  89. #define TURBO_TIMEOUT       3750 //  .. MODED. How many WTD ticks before before dropping down.  If ticks set for 500 ms, then 240 x .5 = 120 seconds.  Max value of 255 unless you change "ticks"
  90.                                 // variable to uint8_t
  91. #define TURBO_RAMP_DOWN         // By default we will start to gradually ramp down, once TURBO_TIMEOUT ticks are reached, 1 PWM_LVL each tick until reaching MODE_TURBO_LOW PWM_LVL
  92.                                 // If commented out, we will step down to MODE_TURBO_LOW once TURBO_TIMEOUT ticks are reached
  93.  
  94. #define FAST_PWM_START      1 // Above what output level should we switch from phase correct to fast-PWM?
  95. //#define DUAL_PWM_START        8 // Above what output level should we switch from the alternate PWM output to both PWM outputs?  Comment out to disable alternate PWM output
  96.  
  97. #define WDT_TIMEOUT         93  // Number of WTD ticks before mode is saved  (about X or 3 seconds). val = X * 1000 / 32
  98.  
  99. #define ADC_LOW             130 // When do we start ramping
  100. #define ADC_CRIT            120 // When do we shut the light off
  101.  
  102. /*
  103.  * =========================================================================
  104.  */
  105.  
  106. //#include <avr/pgmspace.h>
  107. #include <avr/io.h>
  108. #include <util/delay.h>
  109. #include <avr/interrupt.h>
  110. #include <avr/wdt.h>   
  111. #include <avr/eeprom.h>
  112. #include <avr/sleep.h>
  113. //#include <avr/power.h>
  114.  
  115. #define STAR2_PIN   PB0
  116. #define STAR3_PIN   PB4
  117. #define STAR4_PIN   PB3
  118. #define PWM_PIN     PB1
  119. #define VOLTAGE_PIN PB2
  120. #define ADC_CHANNEL 0x01    // MUX 01 corresponds with PB2
  121. #define ADC_DIDR    ADC1D   // Digital input disable bit corresponding with PB2
  122. #define ADC_PRSCL   0x06    // clk/64
  123.  
  124. #define PWM_LVL     OCR0B   // OCR0B is the output compare register for PB1
  125. #define ALT_PWM_LVL OCR0A   // OCR0A is the output compare register for PB0
  126.  
  127. /*
  128.  * global variables
  129.  */
  130.  
  131. // Mode storage
  132. uint8_t eepos = 0;
  133. uint8_t eep[32];
  134.  
  135. volatile uint8_t fade_effect = 0;
  136. volatile uint8_t current_mode = 0;
  137.  
  138.  
  139. // Modes (gets set when the light starts up based on stars)
  140. static uint8_t modes[7];  // Don't need 10, but keeping it high enough to handle all
  141. volatile uint8_t mode_idx = 0;
  142. //int     mode_dir = 0; // 1 or -1. Determined when checking stars. Do we increase or decrease the idx when moving up to a higher mode.
  143. uint8_t mode_cnt = 0;
  144.  
  145. uint8_t lowbatt_cnt = 0;
  146.  
  147. void store_mode_idx(uint8_t lvl) {  //central method for writing (with wear leveling)
  148.     uint8_t oldpos=eepos;
  149.     eepos=(eepos+1)&31;  //wear leveling, use next cell
  150.     // Write the current mode
  151.     EEARL=eepos;  EEDR=lvl; EECR=32+4; EECR=32+4+2;  //WRITE  //32:write only (no erase)  4:enable  2:go
  152.     while(EECR & 2); //wait for completion
  153.     // Erase the last mode
  154.     EEARL=oldpos;           EECR=16+4; EECR=16+4+2;  //ERASE  //16:erase only (no write)  4:enable  2:go
  155. }
  156. inline void read_mode_idx() {
  157.     eeprom_read_block(&eep, 0, 32);
  158.     while((eep[eepos] == 0xff) && (eepos < 32)) eepos++;
  159.     if (eepos < 32) mode_idx = eep[eepos];//&0x10; What the?
  160.     else eepos=0;
  161. }
  162.  
  163. inline void next_mode() {
  164.         mode_idx ++;
  165.         if (mode_idx > (mode_cnt - 1)) {
  166.             // Wrap around
  167.             mode_idx = 0;
  168.         }
  169. }
  170.  
  171. inline void WDT_on() {
  172.     // Setup watchdog timer to only interrupt, not reset
  173.     cli();                          // Disable interrupts
  174.     wdt_reset();                    // Reset the WDT
  175.     WDTCR |= (1<<WDCE) | (1<<WDE);  // Start timed sequence
  176.     #ifdef TICKS_250MS
  177.     WDTCR = (1<<WDTIE) | (1<<WDP2); // Enable interrupt every 250ms
  178.     #else
  179.     WDTCR = (1<<WDTIE) | (1<<WDP0); // Enable interrupt every 32ms   **
  180.     #endif
  181.     sei();                          // Enable interrupts
  182. }
  183.  
  184. inline void WDT_off()
  185. {
  186.     cli();                          // Disable interrupts
  187.     wdt_reset();                    // Reset the WDT
  188.     MCUSR &= ~(1<<WDRF);            // Clear Watchdog reset flag
  189.     WDTCR |= (1<<WDCE) | (1<<WDE);  // Start time d sequence
  190.     WDTCR = 0x00;                   // Disable WDT
  191.     sei();                          // Enable interrupts
  192. }
  193.  
  194. inline void ADC_on() {
  195.     ADMUX  = (1 << REFS0) | (1 << ADLAR) | ADC_CHANNEL; // 1.1v reference, left-adjust, ADC1/PB2
  196.     DIDR0 |= (1 << ADC_DIDR);                           // disable digital input on ADC pin to reduce power consumption
  197.     ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;   // enable, start, prescale
  198. }
  199.  
  200. inline void ADC_off() {
  201.     ADCSRA &= ~(1<<7); //ADC off
  202. }
  203.  
  204.  
  205. #ifdef VOLTAGE_MON
  206. uint8_t low_voltage(uint8_t voltage_val) {
  207.     // Start conversion
  208.     ADCSRA |= (1 << ADSC);
  209.     // Wait for completion
  210.     while (ADCSRA & (1 << ADSC));
  211.     // See if voltage is lower than what we were looking for
  212.     if (ADCH < voltage_val) {
  213.         // See if it's been low for a while
  214.         if (++lowbatt_cnt > 8) {
  215.             lowbatt_cnt = 0;
  216.             return 1;
  217.         }
  218.     } else {
  219.         lowbatt_cnt = 0;
  220.     }
  221.     return 0;
  222. }
  223. #endif
  224.  
  225. void set_output(uint8_t pwm_lvl) {
  226.     fade_effect = 1;
  227.     current_mode =  pwm_lvl;
  228.  
  229.     //PWM_LVL = pwm_lvl;
  230.     // Always set alternate PWM value even if not compiled for dual output as we will use this value
  231.     // throughout the code when trying to see what the current output level is.  Setting this wont affect
  232.     // the output when alternate output is disabled.
  233.     ALT_PWM_LVL = pwm_lvl;
  234. }
  235.  
  236. ISR(WDT_vect) {
  237.     static uint16_t ticks = 0;
  238.     if (ticks < 31767) ticks++;
  239.    //PWM_LVL = PWM_LVL +1;
  240.    
  241.     // modes[mode_cnt++]
  242.  
  243.    
  244. //  set_output(modes[mode_idx]);
  245.  
  246. //  current_mode = modes[mode_idx];
  247.  
  248.     if (fade_effect == 1) {
  249.  
  250.        //PWM_LVL = PWM_LVL +ceil((current_mode - PWM_LVL) / 32);
  251.        if (modes[mode_idx] == MODE_LOW) {
  252.            PWM_LVL = PWM_LVL +1;
  253.             }
  254.        else {
  255.             PWM_LVL = PWM_LVL +3;
  256.        }
  257.  
  258. //     PWM_LVL = PWM_LVL +3;
  259.        if (PWM_LVL > 255) PWM_LVL = 255;
  260.  
  261.        if (PWM_LVL >= current_mode) {
  262.            fade_effect = 0;
  263.        }
  264.     }
  265.  
  266.        
  267.     if (ticks >= WDT_TIMEOUT) {
  268.             // Reset the mode to the start for next time
  269.             store_mode_idx(0);
  270.     }
  271. #ifdef MODE_TURBO  
  272.     //if (ticks == TURBO_TIMEOUT && modes[mode_idx] == MODE_TURBO) { // Doesn't make any sense why this doesn't work
  273.     if (ticks >= TURBO_TIMEOUT && mode_idx == (mode_cnt - 1) && PWM_LVL > MODE_TURBO_LOW) {
  274.         #ifdef TURBO_RAMP_DOWN
  275.         set_output(PWM_LVL - 1);
  276.         #else
  277.         // Turbo mode is always at end
  278.         set_output(MODE_TURBO_LOW);
  279.         //store_mode_idx(mode_idx);
  280.         #endif
  281.     }
  282. #endif
  283.  
  284. }
  285.  
  286. int main(void)
  287. {  
  288.     // All ports default to input, but turn pull-up resistors on for the stars (not the ADC input!  Made that mistake already)
  289.     PORTB = (1 << STAR2_PIN) | (1 << STAR3_PIN) | (1 << STAR4_PIN);
  290.    
  291.     // Set PWM pin to output
  292.     DDRB = (1 << PWM_PIN);
  293.    
  294.     // Turn features on or off as needed
  295.     #ifdef VOLTAGE_MON
  296.        ADC_on();
  297.     #else
  298.        ADC_off();
  299.     #endif
  300.     ACSR   |=  (1<<7); //AC off
  301.    
  302.     // Load up the modes based on stars
  303.     // Always load up the modes array in order of lowest to highest mode
  304.     // 0 being low for soldered, 1 for pulled-up for not soldered
  305.     // Moon
  306.  
  307.     // mode_cnt is increment after array value is assigned
  308.  
  309.     #ifdef MODE_MOON
  310.         modes[mode_cnt++] = MODE_MOON;
  311.     #endif
  312.  
  313.     #ifdef MODE_LOW
  314.         modes[mode_cnt++] = MODE_LOW;
  315.     #endif
  316.     #ifdef MODE_MED
  317.         modes[mode_cnt++] = MODE_MED;
  318.     #endif
  319.     #ifdef MODE_HIGH
  320.         modes[mode_cnt++] = MODE_HIGH;
  321.     #endif
  322.     #ifdef MODE_TURBO
  323.         modes[mode_cnt++] = MODE_TURBO;
  324.     #endif
  325.  
  326.     // Not soldered (1) should enable memory
  327.     //  memory = ((PINB & (1 << STAR4_PIN)) > 0) ? 1 : 0;
  328.     //  memory = 0; // dont need memory
  329.        
  330.     // Enable sleep mode set to Idle that will be triggered by the sleep_mode() command.
  331.     // Will allow us to go idle between WDT interrupts
  332.     set_sleep_mode(SLEEP_MODE_IDLE);
  333.    
  334.     // Determine what mode we should fire up
  335.     // Read the last mode that was saved
  336.     read_mode_idx();
  337.     if (mode_idx&0x10) {
  338.         // Indicates we did a short press last time, go to the next mode
  339.         // Remove short press indicator first
  340.         mode_idx &= 0x0f;
  341.         next_mode(); // Will handle wrap arounds
  342.         fade_effect=1;
  343.     } else {
  344.         // Didn't have a short press, keep the same mode
  345.     }
  346.     // Store mode with short press indicator
  347.     store_mode_idx(mode_idx|0x10);
  348.    
  349.     WDT_on();
  350.    
  351.     // Now just fire up the mode
  352.     // Set timer to do PWM for correct output pin and set prescaler timing
  353.     if (modes[mode_idx] > FAST_PWM_START) {
  354.         #ifdef DUAL_PWM_START
  355.         TCCR0A = 0b10100011; // fast-PWM both outputs
  356.         #else
  357.         TCCR0A = 0b00100011; // fast-PWM normal output
  358.         #endif
  359.     } else {
  360.         #ifdef DUAL_PWM_START
  361.         TCCR0A = 0b10100001; // phase corrected PWM both outputs
  362.         #else
  363.         TCCR0A = 0b00100001; // phase corrected PWM normal output
  364.         #endif
  365.     }
  366.     TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
  367.    
  368.     // Start with minimum mode to speed up fade effects
  369.     PWM_LVL = 15;
  370.        
  371.     set_output(modes[mode_idx]);
  372. //  current_mode = modes[mode_idx];
  373.  
  374.    
  375.     uint8_t i = 0;
  376.     uint8_t hold_pwm;
  377.     while(1) {
  378.     #ifdef VOLTAGE_MON
  379.         if (low_voltage(ADC_LOW)) {
  380.             // We need to go to a lower level
  381.             if (mode_idx == 0 && ALT_PWM_LVL <= modes[mode_idx]) {
  382.                 // Can't go any lower than the lowest mode
  383.                 // Wait until we hit the critical level before flashing 10 times and turning off
  384.                 while (!low_voltage(ADC_CRIT));
  385.                 i = 0;
  386.                 while (i++<10) {
  387.                     set_output(0);
  388.                     _delay_ms(250);
  389.                     set_output(modes[0]);
  390.                     _delay_ms(500);
  391.                 }
  392.                 // Turn off the light
  393.                 set_output(0);
  394.                 // Disable WDT so it doesn't wake us up
  395.                 WDT_off();
  396.                 // Power down as many components as possible
  397.                 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  398.                 sleep_mode();
  399.             } else {
  400.                 // Flash 3 times before lowering
  401.                 hold_pwm = ALT_PWM_LVL;
  402.                 i = 0;
  403.                 while (i++<3) {
  404.                     set_output(0);
  405.                     _delay_ms(250);
  406.                     set_output(hold_pwm);
  407.                     _delay_ms(500);
  408.                 }
  409.                 // Lower the mode by half, but don't go below lowest level
  410.                 if ((ALT_PWM_LVL >> 1) < modes[0]) {
  411.                     set_output(modes[0]);
  412.                     mode_idx = 0;
  413.                 } else {
  414.                     set_output(ALT_PWM_LVL >> 1);
  415.                 }                  
  416.                 // See if we should change the current mode level if we've gone under the current mode.
  417.                 if (ALT_PWM_LVL < modes[mode_idx]) {
  418.                     // Lower our recorded mode
  419.                     mode_idx--;
  420.                 }
  421.             }
  422.             // Wait 3 seconds before lowering the level again
  423.             _delay_ms(3000);
  424.         }
  425.     #endif
  426.         sleep_mode();
  427.     }
  428.  
  429.     return 0; // Standard Return Code
  430. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement