Advertisement
gchart

Babka - Fet+1

Aug 19th, 2019
71
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 17.04 KB | None | 0 0
  1. /*
  2.  
  3.  * ====================================================================
  4.  *  "Babka" firmware by gChart (Gabriel Hart)
  5.  *  Last updated: 2017-03-20 (v3)
  6.  *
  7.  *  This firmware is based on "Biscotti" by ToyKeeper and is intended
  8.  *  for single-channel attiny13a drivers such as Qlite and nanjc105d.
  9.  *
  10.  *  Change log:
  11.  *    v2: - Added more mode groups
  12.  *        - Went from 2 turbo options to 1 (on or off)
  13.  *        - Code efficiency improvements
  14.  *        - Moved some chunks of code to inline functions for readability
  15.  *        - Changed lowest modes to properly use PHASE
  16.  *        - Changed a couple variables to register variables
  17.  *        - Added a second fast_presses variable for robustness
  18.  *
  19.  *    v3: - Code efficiency improvements (saved like 58 bytes)
  20.  *        - Split toggle function into two separate functions
  21.  *        - More register variables, now possible b/c of the two toggle functions
  22.  *        - Replaced fast_presses variables with an array of size NUM_FP_BYTES
  23.  *        - Variable length mode groups
  24.  *        - More mode groups
  25.  *        - Turbo ramp-down option (disable by commenting TURBO_RAMP_DOWN flag, ~10 bytes)
  26.  *
  27.  * ====================================================================
  28.  *
  29.  * 12 or more fast presses > Configuration mode
  30.  *
  31.  * If hidden modes are enabled, perform a triple-click to enter the hidden modes.
  32.  * Tap once to advance through the hidden modes.  Once you advance past the last
  33.  * hidden mode, you will return to the first non-hidden mode.
  34.  *
  35.  * Configuration Menu
  36.  *   1: Mode Groups:
  37.  *     1: 100
  38.  *     2: 20, 100
  39.  *     3: 5, 35, 100
  40.  *     4: 1, 20, 100
  41.  *     5: 1, 10, 35, 100
  42.  *     6: 100, 35, 10, 1
  43.  *     7: 100, 20, 1
  44.  *     8: 100, 35, 5
  45.  *     9: 100, 20
  46.  *     10: 100, Strobe
  47.  *     11: 20, 100, Strobe
  48.  *     12: 5, 35, 100, Strobe
  49.  *     13: 1, 10, 35, 100, Strobe
  50.  *     14: 100, 35, 10, 1, Strobe
  51.  *     15: 100, 35, 5, Strobe
  52.  *     16: 100, 20, Strobe
  53.  *   2: Memory Toggle
  54.  *   3: Hidden Strobe Modes Toggle (Strobe, Beacon, SOS)
  55.  *   4: Hidden Batt Check Toggle (if strobes are enabled, Batt Check appears at the end of them)
  56.  *   5: Turbo Timer Toggle (turbo steps down to 50%)
  57.  *   6: Reset to default configuration
  58.  *  
  59.  * ====================================================================
  60.  *
  61.  * "Biscotti" firmware (attiny13a version of "Bistro")
  62.  * This code runs on a single-channel driver with attiny13a MCU.
  63.  * It is intended specifically for nanjg 105d drivers from Convoy.
  64.  *
  65.  * Copyright (C) 2015 Selene Scriven
  66.  *
  67.  * This program is free software: you can redistribute it and/or modify
  68.  * it under the terms of the GNU General Public License as published by
  69.  * the Free Software Foundation, either version 3 of the License, or
  70.  * (at your option) any later version.
  71.  *
  72.  * This program is distributed in the hope that it will be useful,
  73.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  74.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  75.  * GNU General Public License for more details.
  76.  *
  77.  * You should have received a copy of the GNU General Public License
  78.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  79.  *
  80.  *
  81.  * ATTINY13 Diagram
  82.  *            ----
  83.  *          -|1  8|- VCC
  84.  *      OTC -|2  7|- Voltage ADC
  85.  *          -|3  6|- PWM (FET)
  86.  *      GND -|4  5|- PWM (1x7135)
  87.  *            ----
  88.  *
  89.  * FUSES
  90.  *    I use these fuse settings on attiny13
  91.  *    Low:  0x75
  92.  *    High: 0xff
  93.  *
  94.  * CALIBRATION
  95.  *
  96.  *   To find out what values to use, flash the driver with battcheck.hex
  97.  *   and hook the light up to each voltage you need a value for.  This is
  98.  *   much more reliable than attempting to calculate the values from a
  99.  *   theoretical formula.
  100.  *
  101.  */
  102. // Choose your MCU here, or in the build script
  103. #define ATTINY 13
  104. #define FET_7135_LAYOUT  // specify an I/O pin layout
  105. // Also, assign I/O pins in this file:
  106. #include "../tk-attiny.h"
  107.  
  108. #define DEFAULTS 0b00010010  // Mode group 3 with memory, no blinkies, no battcheck, no turbo timer
  109. //#define DEFAULTS 0b10010010  // Mode group 3 with memory, no blinkies, no battcheck, with turbo timer
  110.  
  111. #define FAST  0xA3        // fast PWM both channels
  112. #define PHASE 0xA1        // phase-correct PWM both channels
  113. #define VOLTAGE_MON      // Comment out to disable LVP
  114. #define USE_BATTCHECK      // Enable battery check mode
  115. //#define BATTCHECK_4bars    // up to 4 blinks
  116. #define BATTCHECK_8bars  // up to 8 blinks
  117.  
  118. #define RAMP_SIZE  6
  119. //#define RAMP_PWM_7135   6, 23, 255,   0
  120. //#define RAMP_PWM_FET    0,  0,  18, 255
  121. #define RAMP_PWM_7135   5,  50, 200, 255, 255,   0
  122. #define RAMP_PWM_FET    0,   0,   6,  26,  50, 255
  123. // approximate amps: 0.007, 0.07, 0.38, 0.8, 1.2, 4.5
  124.  
  125. #define BLINK_BRIGHTNESS    3 // output to use for blinks on battery check (and other modes)
  126. #define BLINK_SPEED      750 // ms per normal-speed blink
  127.  
  128. #define TURBO_MINUTES 5 // when turbo timer is enabled, how long before stepping down
  129. #define TICKS_PER_MINUTE 120 // used for Turbo Timer timing
  130. #define TURBO_LOWER 128  // the PWM level to use when stepping down
  131. #define TURBO    RAMP_SIZE  // Convenience code for turbo mode
  132. #define TURBO_RAMP_DOWN  // disable this to jump right from turbo to the stepdown, no ramping
  133.  
  134. #define GROUP_SELECT_MODE 252
  135.  
  136. // These need to be in sequential order, no gaps.
  137. // Make sure to update FIRST_BLINKY and LAST_BLINKY as needed.
  138. // BATT_CHECK should be the last blinky, otherwise the non-blinky
  139. // mode groups will pick up any blinkies after BATT_CHECK
  140. #define FIRST_BLINKY 240
  141. #define STROBE  240
  142. #define BEACON 241
  143. #define SOS 242
  144. #define BATT_CHECK 243
  145. #define LAST_BLINKY 243
  146.  
  147. // Calibrate voltage and OTC in this file:
  148. #include "../tk-calibration.h"
  149.  
  150. /*
  151.  * =========================================================================
  152.  */
  153.  
  154. // Ignore a spurious warning, we did the cast on purpose
  155. #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
  156.  
  157. #include <avr/pgmspace.h>
  158. #include <avr/interrupt.h>
  159. #include <avr/eeprom.h>
  160. #include <avr/sleep.h>
  161. #include <string.h>
  162.  
  163. #define OWN_DELAY          // Don't use stock delay functions.
  164. #define USE_DELAY_S      // Also use _delay_s(), not just _delay_ms()
  165. #include "../tk-delay.h"
  166.  
  167. #include "../tk-voltage.h"
  168.  
  169. // borrowed some register variable settings / ideas from Flintrock
  170. register uint8_t options asm("r6");   //uint8_t options = 0;
  171. register uint8_t mode_idx asm("r7");  //uint8_t mode_idx = 0;    
  172. register uint8_t eepos asm("r8");     //uint8_t eepos = 0;
  173. uint8_t num_modes;
  174.  
  175. uint8_t fast_presses __attribute__ ((section (".noinit")));
  176.  
  177. #define NUM_MODEGROUPS 16 // Can define up to 16 groups
  178. PROGMEM const uint8_t modegroups[] = {
  179.     6, 0,
  180.     4, 6, 0,
  181.     2, 5, 6, 0,
  182.     1, 4, 6, 0,
  183.     1, 3, 5, 6, 0,
  184.     6, 5, 3, 1, 0,
  185.     6, 4, 1, 0,
  186.     6, 5, 2, 0,
  187.     6, 4, 0,
  188.     6, STROBE, 0,
  189.     4, 6, STROBE, 0,
  190.   2, 5, 6, STROBE, 0,
  191.     1, 3, 5, 6, STROBE, 0,
  192.   6, 5, 3, 1, STROBE, 0,
  193.     6, 5, 2, STROBE, 0,
  194.     6, 4, STROBE, 0,
  195. };
  196. uint8_t modes[] = { 0,0,0,0,0 };  // make sure this is long enough...
  197.  
  198. // Modes (gets set when the light starts up based on saved config values)
  199. PROGMEM const uint8_t ramp_PWM_7135[]  = { RAMP_PWM_7135 };
  200. PROGMEM const uint8_t ramp_PWM_FET[]  = { RAMP_PWM_FET };
  201.  
  202. inline uint8_t modegroup_number()     { return (options     ) & 0b00001111; }
  203. inline uint8_t memory_is_enabled()    { return (options >> 4) & 0b00000001; }
  204. inline uint8_t blinkies_are_enabled() { return (options >> 5) & 0b00000001; }
  205. inline uint8_t battcheck_is_enabled() { return (options >> 6) & 0b00000001; }
  206. inline uint8_t ttimer_is_enabled()    { return (options >> 7) & 0b00000001; }
  207.  
  208. void save_mode() {  // save the current mode index (with wear leveling)
  209.     uint8_t oldpos=eepos;
  210.     eepos = (eepos+1) & ((EEPSIZE/2)-1);  // wear leveling, use next cell
  211.     eeprom_write_byte((uint8_t *)(eepos), mode_idx);  // save current state
  212.     eeprom_write_byte((uint8_t *)(oldpos), 0xff);     // erase old state
  213. }
  214.  
  215. #define OPT_options (EEPSIZE-1)
  216. void save_state() {  // central method for writing complete state
  217.     save_mode();
  218.     eeprom_write_byte((uint8_t *)OPT_options, options);
  219. }
  220.  
  221. inline void reset_state() {
  222.     mode_idx = 0;
  223.     options = DEFAULTS;  // 3 brightness levels with memory
  224.     save_state();
  225. }
  226.  
  227. inline void restore_state() {
  228.     uint8_t eep;
  229.     uint8_t first = 1;
  230.     uint8_t i;
  231.     // find the mode index data
  232.     for(i=0; i<(EEPSIZE-6); i++) {
  233.         eep = eeprom_read_byte((const uint8_t *)i);
  234.         if (eep != 0xff) {
  235.             eepos = i;
  236.             mode_idx = eep;
  237.             first = 0;
  238.             break;
  239.         }
  240.     }
  241.     // if no mode_idx was found, assume this is the first boot
  242.     if (first) {
  243.         reset_state();
  244.         return;
  245.     }
  246.  
  247.     // load other config values
  248.     options = eeprom_read_byte((uint8_t *)OPT_options);
  249. }
  250.  
  251. inline void next_mode() {
  252.     mode_idx++;
  253.    
  254.     if(fast_presses == 3 && mode_idx <= num_modes) {  // triple-tap from a solid mode
  255.         if( blinkies_are_enabled() ) mode_idx = FIRST_BLINKY; // if blinkies enabled, go to first one
  256.         else if( battcheck_is_enabled() ) mode_idx = BATT_CHECK; // else if battcheck enabled, go to it
  257.     }
  258.    
  259.     // if we hit the end of the solid modes or the blinkies (or battcheck if disabled), go to first solid mode
  260.     if ( (mode_idx == num_modes) || (mode_idx > LAST_BLINKY) || (mode_idx == BATT_CHECK && !battcheck_is_enabled() )) {
  261.         mode_idx = 0;
  262.     }
  263.    
  264. }
  265.  
  266. inline void count_modes() {
  267.     uint8_t group = 0, mode, i, mc=0;
  268.     uint8_t *dest = modes;
  269.     const uint8_t *src = modegroups;
  270.     for(i=0; i<sizeof(modegroups); i++) {
  271.         mode = pgm_read_byte(src+i);
  272.         // if we hit a 0, that means we're moving on to the next group
  273.         if (mode==0) { group++; }
  274.         // else if we're in the right group, store the mode and increase the mode count
  275.         else if (group == modegroup_number()) { *dest++ = mode; mc++; }
  276.     }
  277.     num_modes = mc;
  278. }
  279.  
  280. void set_led_off() {
  281.     TCCR0A = PHASE;
  282.     PWM_LVL = 0;
  283.     ALT_PWM_LVL = 0;
  284. }
  285.  
  286. void set_output(uint8_t pwm1, uint8_t pwm2) {
  287.     //if (pwm1 == 255) { TCCR0A = PHASE; }
  288.     //if (((pwm1==0) && (pwm2==0)) || ( pwm1 == 255 )) { TCCR0A = PHASE; }
  289.     if ( pwm1 == 255 ) { TCCR0A = PHASE; }
  290.     else { TCCR0A = FAST; }
  291.     PWM_LVL = pwm1;
  292.     ALT_PWM_LVL = pwm2;
  293. }
  294.  
  295. void set_level(uint8_t level) {
  296.     set_output(pgm_read_byte(ramp_PWM_FET + level - 1), pgm_read_byte(ramp_PWM_7135  + level - 1));
  297. }
  298.  
  299. void blink(uint8_t val, uint16_t speed)
  300. {
  301.     for (; val>0; val--)
  302.     {
  303.         set_level(BLINK_BRIGHTNESS);
  304.         _delay_ms(speed);
  305.         set_led_off(); //set_output(0,0);
  306.         _delay_ms(speed);
  307.         _delay_ms(speed);
  308.     }
  309. }
  310.  
  311. inline void toggle_mode(uint8_t value, uint8_t num) {
  312.     blink(num, BLINK_SPEED/4);  // indicate which option number this is
  313.     uint8_t temp = mode_idx;
  314.     mode_idx = value;
  315.     save_state();
  316.     blink(32, 500/32); // "buzz" for a while to indicate the active toggle window
  317.    
  318.     // if the user didn't click, reset the value and return
  319.     mode_idx = temp;
  320.     save_state();
  321.     _delay_s();
  322. }
  323.  
  324. void toggle_options(uint8_t value, uint8_t num) {
  325.     blink(num, BLINK_SPEED/4);  // indicate which option number this is
  326.     uint8_t temp = options;
  327.     options = value;
  328.     save_state();
  329.     blink(32, 500/32); // "buzz" for a while to indicate the active toggle window
  330.    
  331.     // if the user didn't click, reset the value and return
  332.     options = temp;
  333.     save_state();
  334.     _delay_s();
  335. }
  336.  
  337. int main(void)
  338. {
  339.    
  340.     uint8_t cap_val;
  341.  
  342.     // Read the off-time cap *first* to get the most accurate reading
  343.     // Start up ADC for capacitor pin
  344.     DIDR0 |= (1 << CAP_DIDR);                           // disable digital input on ADC pin to reduce power consumption
  345.     ADMUX  = (1 << V_REF) | (1 << ADLAR) | CAP_CHANNEL; // 1.1v reference, left-adjust, ADC3/PB3
  346.     ADCSRA = (1 << ADEN ) | (1 << ADSC ) | ADC_PRSCL;   // enable, start, prescale
  347.  
  348.     // Wait for completion
  349.     while (ADCSRA & (1 << ADSC));
  350.     // Start again as datasheet says first result is unreliable
  351.     ADCSRA |= (1 << ADSC);
  352.     // Wait for completion
  353.     while (ADCSRA & (1 << ADSC));
  354.     cap_val = ADCH; // save this for later
  355.  
  356.     DDRB |= (1 << PWM_PIN);  // Set PWM pin to output, enable main channel
  357.     DDRB |= (1 << ALT_PWM_PIN); // enable second channel
  358.     TCCR0A = PHASE; // Set timer to do PWM for correct output pin and set prescaler timing
  359.     TCCR0B = 0x01; // Set timer to do PWM for correct output pin and set prescaler timing
  360.    
  361.     // Turn off LEDs initially until we can figure out what they're supposed to be at.
  362.     // Hopefully this will address the spike I see when changing modes.
  363.     // set_led_off(); // set_output(0,0);
  364.  
  365.     restore_state(); // Read config values and saved state
  366.  
  367.     count_modes(); // Enable the current mode group
  368.      // check button press time, unless we're in group selection mode
  369.     if (mode_idx != GROUP_SELECT_MODE) {
  370.         if ( cap_val > CAP_SHORT ) { // short press
  371.             fast_presses++;
  372.             next_mode(); // Will handle wrap arounds
  373.         } else { // Long press, keep the same mode
  374.             fast_presses=0;
  375.             if( !memory_is_enabled() ) {mode_idx = 0;}  // if memory is turned off, set mode_idx to 0
  376.         }
  377.     }
  378.     save_mode();
  379.    
  380.     // Charge up the capacitor by setting CAP_PIN to output
  381.     DDRB  |= (1 << CAP_PIN);    // Output
  382.     PORTB |= (1 << CAP_PIN);    // High
  383.  
  384.     // Turn features on or off as needed
  385.     #ifdef VOLTAGE_MON
  386.     ADC_on();
  387.     #else
  388.     ADC_off();
  389.     #endif
  390.  
  391.     uint8_t output;
  392.     uint8_t i = 0;
  393.     uint16_t ticks = 0;
  394. #ifdef TURBO_RAMP_DOWN
  395.     uint8_t adj_output = 255;
  396. #endif
  397.    
  398. #ifdef VOLTAGE_MON
  399.     uint8_t lowbatt_cnt = 0;
  400.     uint8_t voltage;
  401.     ADCSRA |= (1 << ADSC); // Make sure voltage reading is running for later
  402. #endif
  403.  
  404.     if(mode_idx > num_modes) { output = mode_idx; }  // special modes, override output
  405.     else { output = modes[mode_idx]; }
  406.    
  407.     while(1) {
  408.         if (fast_presses >= 15) {  // Config mode if 12 or more fast presses
  409.             _delay_s();    // wait for user to stop fast-pressing button
  410.             fast_presses=0; // exit this mode after one use
  411.  
  412.             toggle_mode(GROUP_SELECT_MODE, 1); // Enter the mode group selection mode?
  413.             toggle_options((options ^ 0b00010000), 2); // memory
  414.             toggle_options((options ^ 0b00100000), 3); // hidden blinkies
  415.             toggle_options((options ^ 0b01000000), 4); // hidden battcheck
  416.             toggle_options((options ^ 0b10000000), 5); // turbo timer
  417.             toggle_options(DEFAULTS, 6); // reset to defaults
  418.         }
  419.         else if (output == STROBE) { // 10Hz tactical strobe
  420.             for(i=0;i<8;i++) {
  421.                 set_level(RAMP_SIZE);
  422.                 _delay_ms(33);
  423.                 set_led_off(); // set_output(0,0);
  424.                 _delay_ms(67);
  425.             }
  426.         }
  427.         else if (output == BEACON) {
  428.             set_level(RAMP_SIZE);
  429.             _delay_ms(10);
  430.             set_led_off(); // set_output(0,0);
  431.             _delay_s(); _delay_s();
  432.         }
  433.         else if (output == SOS) {  // dot = 1 unit, dash = 3 units, space betwen parts of a letter = 1 unit, space between letters = 3 units
  434.             #define SOS_SPEED 200  // these speeds aren't perfect, but they'll work for the [never] times they'll actually get used
  435.             blink(3, SOS_SPEED); // 200 on, 400 off, 200 on, 400 off, 200 on, 400 off
  436.             _delay_ms(SOS_SPEED);  // wait for 200
  437.             blink(3, SOS_SPEED*5/2);  // 500 on, 1000 off, 500 on, 1000 off, 500 on, 1000 off (techically too long on that last off beat, but oh well)
  438.             blink(3, SOS_SPEED);  // 200 on, 400 off, 200 on, 400 off, 200 on, 400 off
  439.             _delay_s(); _delay_s();
  440.         }
  441.         else if (output == BATT_CHECK) {
  442.              blink(battcheck(), BLINK_SPEED/4);
  443.              _delay_s(); _delay_s();
  444.         }
  445.         else if (output == GROUP_SELECT_MODE) {
  446.             mode_idx = 0; // exit this mode after one use
  447.  
  448.             for(i=0; i<NUM_MODEGROUPS; i++) {
  449.                 toggle_options(((options & 0b11110000) | i), i+1);
  450.             }
  451.             _delay_s();
  452.         }
  453.         else { // regular solid mode
  454.             ticks ++; // count ticks for turbo timer
  455.             set_level(output);
  456.            
  457.             if ((output == TURBO) && ( ttimer_is_enabled() ) && (ticks > (TURBO_MINUTES * TICKS_PER_MINUTE))) {
  458.     #ifdef TURBO_RAMP_DOWN
  459.                 if (adj_output > TURBO_LOWER) { adj_output = adj_output - 2; }
  460.                 set_output(adj_output,0);
  461.     #else
  462.                 set_output(TURBO_LOWER,0);
  463.     #endif
  464.             }
  465.             // moved this stuff at the top of this else clause to see if we can get rid of the occasional spike when switching modes
  466.             //else {
  467.             //  ticks ++; // count ticks for turbo timer
  468.             //  set_level(output);
  469.             //}
  470.  
  471.             _delay_ms(500);  // Otherwise, just sleep.
  472.  
  473.         }
  474.         fast_presses=0;
  475. #ifdef VOLTAGE_MON
  476.         if (ADCSRA & (1 << ADIF)) {  // if a voltage reading is ready
  477.             voltage = ADCH;  // get the waiting value
  478.    
  479.             if (voltage < ADC_LOW) { // See if voltage is lower than what we were looking for
  480.                 lowbatt_cnt ++;
  481.             } else {
  482.                 lowbatt_cnt = 0;
  483.             }
  484.            
  485.             if (lowbatt_cnt >= 8) {  // See if it's been low for a while, and maybe step down
  486.                 //set_output(0);  _delay_ms(100); // blink on step-down:
  487.  
  488.                 if (output > RAMP_SIZE) {  // blinky modes
  489.                     //output = RAMP_SIZE / 2; // step down from blinky modes to medium
  490.                     output = RAMP_SIZE - 1; // step down from blinky modes to mode below turbo
  491.                 } else if (output > 1) {  // regular solid mode
  492.                     output = output - 1; // step down from solid modes somewhat gradually
  493.                 } else { // Already at the lowest mode
  494.                     set_led_off(); // set_output(0,0); // Turn off the light
  495.                     set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Power down as many components as possible
  496.                     sleep_mode();
  497.                 }
  498.                 set_level(output);
  499.                 lowbatt_cnt = 0;
  500.                 _delay_s(); // Wait before lowering the level again
  501.             }
  502.  
  503.             ADCSRA |= (1 << ADSC); // Make sure conversion is running for next time through
  504.         }
  505. #endif  // ifdef VOLTAGE_MON
  506.     }
  507. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement