Advertisement
gchart

Ramping UI

Mar 16th, 2017
116
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 10.72 KB | None | 0 0
  1. /*
  2.  
  3.  * ====================================================================
  4.  *  Ramping UI for nanjg105c and a clicky switch by gChart (Gabriel Hart)
  5.  *
  6.  *  This firmware has its roots loosely based  on "Biscotti" by ToyKeeper
  7.  *
  8.  * ====================================================================
  9.  *
  10.  * 12 or more fast presses > Configuration mode
  11.  *
  12.  * Configuration Menu
  13.  *   1: Memory toggle
  14.  *   2: Stop-at-the-top toggle (without any user interaction, stops itself at turbo)
  15.  *   3: Turbo Timer Toggle (turbo steps down to 50%)
  16.  *   4: Reset to default configuration
  17.  *  
  18.  * ====================================================================
  19.  *
  20.  * "Biscotti" firmware (attiny13a version of "Bistro")
  21.  * This code runs on a single-channel driver with attiny13a MCU.
  22.  * It is intended specifically for nanjg 105d drivers from Convoy.
  23.  *
  24.  * Copyright (C) 2015 Selene Scriven
  25.  *
  26.  * This program is free software: you can redistribute it and/or modify
  27.  * it under the terms of the GNU General Public License as published by
  28.  * the Free Software Foundation, either version 3 of the License, or
  29.  * (at your option) any later version.
  30.  *
  31.  * This program is distributed in the hope that it will be useful,
  32.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  33.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  34.  * GNU General Public License for more details.
  35.  *
  36.  * You should have received a copy of the GNU General Public License
  37.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  38.  *
  39.  *
  40.  * ATTINY13 Diagram
  41.  *         ----
  42.  *       -|1  8|- VCC
  43.  *       -|2  7|- Voltage ADC
  44.  *       -|3  6|-
  45.  *   GND -|4  5|- PWM (Nx7135)
  46.  *         ----
  47.  *
  48.  * FUSES
  49.  *    I use these fuse settings on attiny13
  50.  *    Low:  0x75
  51.  *    High: 0xff
  52.  *
  53.  * CALIBRATION
  54.  *
  55.  *   To find out what values to use, flash the driver with battcheck.hex
  56.  *   and hook the light up to each voltage you need a value for.  This is
  57.  *   much more reliable than attempting to calculate the values from a
  58.  *   theoretical formula.
  59.  *
  60.  *   Same for off-time capacitor values.  Measure, don't guess.
  61.  */
  62. // Choose your MCU here, or in the build script
  63. #define ATTINY 13
  64. #define NANJG_LAYOUT  // specify an I/O pin layout
  65. // Also, assign I/O pins in this file:
  66. #include "../tk-attiny.h"
  67.  
  68. #define DEFAULTS 0b00000100 // No memory, doesn't stop at the top, turbo timer is enabled
  69.  
  70. #define FAST  0xA3        // fast PWM both channels
  71. #define PHASE 0xA1        // phase-correct PWM both channels
  72. #define VOLTAGE_MON      // Comment out to disable LVP
  73.  
  74. #define BLINK_BRIGHTNESS    40 // output to use for blinks in config mode
  75. #define BLINK_SPEED      750 // ms per normal-speed blink
  76.  
  77. #define USE_BATTCHECK      // Enable battery check mode
  78. #define BATTCHECK_8bars  // up to 8 blinks
  79.  
  80. #define TURBO_MINUTES 5 // when turbo timer is enabled, how long before stepping down
  81. #define TICKS_PER_MINUTE 120 // used for Turbo Timer timing
  82. #define TURBO_LOWER 128  // the PWM level to use when stepping down
  83. #define TURBO_THRESHOLD sizeof(ramp_values)-5 // Min output level that we consider to be turbo?
  84.  
  85. #define RAMP_TIME  5  // number of seconds to go from min brightness to max brightness
  86.  
  87. #define RAMP_SIZE sizeof(ramp_values)
  88. //#define RAMP_VALUES 1,1,1,1,1,2,2,2,2,3,3,4,5,5,6,7,8,9,10,11,13,14,16,18,20,22,24,26,29,32,34,38,41,44,48,51,55,60,64,68,73,78,84,89,95,101,107,113,120,127,134,142,150,158,166,175,184,193,202,212,222,233,244,255
  89. #define RAMP_VALUES 5,5,5,5,5,6,6,6,6,7,7,8,8,9,10,11,12,13,14,15,17,18,20,22,23,25,28,30,32,35,38,41,44,47,51,55,59,63,67,71,76,81,86,92,97,103,109,116,122,129,136,144,151,159,167,176,185,194,203,213,223,233,244,255
  90.  
  91. // Calibrate voltage and OTC in this file:
  92. #include "../tk-calibration.h"
  93.  
  94. /*
  95.  * =========================================================================
  96.  */
  97.  
  98. // Ignore a spurious warning, we did the cast on purpose
  99. #pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
  100.  
  101. #include <avr/pgmspace.h>
  102. #include <avr/interrupt.h>
  103. #include <avr/eeprom.h>
  104. #include <avr/sleep.h>
  105. #include <string.h>
  106.  
  107. #define OWN_DELAY          // Don't use stock delay functions.
  108. #define USE_DELAY_S      // Also use _delay_s(), not just _delay_ms()
  109. #include "../tk-delay.h"
  110.  
  111. #include "../tk-voltage.h"
  112.  
  113. register uint8_t options asm("r6");
  114. register uint8_t eepos asm("r7");
  115.  
  116. #define NUM_FP_BYTES 3
  117. uint8_t fast_presses[NUM_FP_BYTES] __attribute__ ((section (".noinit")));
  118.  
  119. #define RAMPING_UP 0
  120. #define RAMPING_DOWN 1
  121. uint8_t ramp_direction __attribute__ ((section (".noinit")));
  122. uint8_t ramp_stopped __attribute__ ((section (".noinit")));
  123. uint8_t output __attribute__ ((section (".noinit")));
  124. uint8_t output_in_eeprom;
  125.  
  126. PROGMEM const uint8_t ramp_values[]  = { RAMP_VALUES };
  127.  
  128. inline uint8_t memory_is_enabled()   { return (options     ) & 0b00000001; }
  129. inline uint8_t stop_at_the_top()     { return (options >> 1) & 0b00000001; }
  130. inline uint8_t ttimer_is_enabled()   { return (options >> 2) & 0b00000001; }
  131.  
  132. void save_output() {  // save the current output level (with wear leveling)
  133.     uint8_t oldpos=eepos;
  134.     eepos = (eepos+1) & ((EEPSIZE/2)-1);  // wear leveling, use next cell
  135.     eeprom_write_byte((uint8_t *)(eepos), output);  // save current state
  136.     eeprom_write_byte((uint8_t *)(oldpos), 0xff);     // erase old state
  137. }
  138.  
  139. #define OPT_options (EEPSIZE-1)
  140. void save_state() {  // central method for writing complete state
  141.     save_output();
  142.     eeprom_write_byte((uint8_t *)OPT_options, options);
  143. }
  144.  
  145. inline void reset_state() {
  146.     output_in_eeprom = 1;
  147.     options = DEFAULTS;  // 3 brightness levels with memory
  148.     save_state();
  149. }
  150.  
  151. inline void restore_state() {
  152.     uint8_t eep;
  153.     uint8_t first = 1;
  154.     uint8_t i;
  155.     // find the output level data
  156.     for(i=0; i<(EEPSIZE-6); i++) {
  157.         eep = eeprom_read_byte((const uint8_t *)i);
  158.         if (eep != 0xff) {
  159.             eepos = i;
  160.             output_in_eeprom = eep;
  161.             first = 0;
  162.             break;
  163.         }
  164.     }
  165.     // if no output level was found, assume this is the first boot
  166.     if (first) {
  167.         reset_state();
  168.         return;
  169.     }
  170.  
  171.     // load other config values
  172.     options = eeprom_read_byte((uint8_t *)OPT_options);
  173. }
  174.  
  175. void set_pwm(uint8_t pwm) {
  176.     TCCR0A = PHASE;
  177.     TCCR0B = 0x01;
  178.     PWM_LVL = pwm;
  179. }
  180.  
  181. void set_level(uint8_t level) {
  182.     if (level == 0) { set_pwm(0); }
  183.     else { set_pwm( pgm_read_byte(ramp_values + level - 1) ); }
  184. }
  185.  
  186. void blink(uint8_t val, uint16_t speed)
  187. {
  188.     for (; val>0; val--)
  189.     {
  190.         set_pwm(BLINK_BRIGHTNESS);
  191.         _delay_ms(speed);
  192.         set_pwm(0);
  193.         _delay_ms(speed);
  194.         _delay_ms(speed);
  195.     }
  196. }
  197.  
  198. void toggle_options(uint8_t value, uint8_t num) {
  199.     blink(num, BLINK_SPEED/4);  // indicate which option number this is
  200.     uint8_t temp = options;
  201.     options = value;
  202.     save_state();
  203.     blink(32, 500/32); // "buzz" for a while to indicate the active toggle window
  204.    
  205.     // if the user didn't click, reset the value and return
  206.     options = temp;
  207.     save_state();
  208.     _delay_s();
  209. }
  210.  
  211. inline uint8_t we_did_a_fast_press() {
  212.     uint8_t i = NUM_FP_BYTES-1;
  213.     while (i && (fast_presses[i] == fast_presses[i-1] )){ --i; }
  214.     return !i;
  215. }
  216. inline void increment_fast_presses() {
  217.     uint8_t i;
  218.     for(i=0; i<NUM_FP_BYTES; i++) { fast_presses[i]++; }
  219. }
  220.  
  221. void reset_fast_presses() {
  222.     uint8_t i;
  223.     for(i=0; i<NUM_FP_BYTES; i++) { fast_presses[i] = 0; }
  224. }
  225.  
  226. int main(void)
  227. {
  228.  
  229.     DDRB |= (1 << PWM_PIN);  // Set PWM pin to output, enable main channel
  230.    
  231.     restore_state(); // Read config values and saved state
  232.  
  233.   if ( we_did_a_fast_press() ) {
  234.     increment_fast_presses();
  235.         ramp_stopped = !ramp_stopped;
  236.        
  237.         if(fast_presses[0] == 2) {  // jump to turbo on a double-tap from anywhere
  238.             output = RAMP_SIZE;
  239.             ramp_stopped = 1;
  240.         }
  241.        
  242.     if(ramp_stopped) { save_output(); }
  243.  
  244.   } else { // Long press
  245.     reset_fast_presses();
  246.         ramp_direction = RAMPING_UP;
  247.     if( memory_is_enabled() ) {
  248.       output = output_in_eeprom;
  249.             ramp_stopped = 1;
  250.     }
  251.     else {
  252.       output = 1;
  253.             ramp_stopped = 0;
  254.     }
  255.   }
  256.  
  257.     #ifdef VOLTAGE_MON
  258.     ADC_on();
  259.     #else
  260.     ADC_off();
  261.     #endif
  262.  
  263.     uint16_t ticks = 0;
  264.    
  265. #ifdef VOLTAGE_MON
  266.     uint8_t lowbatt_cnt = 0;
  267.     uint8_t voltage;
  268.     ADCSRA |= (1 << ADSC); // Make sure voltage reading is running for later
  269. #endif
  270.    
  271.     while(1) {
  272.         if (fast_presses[0] >= 12) {  // Config mode if 12 or more fast presses
  273.             _delay_s();    // wait for user to stop fast-pressing button
  274.             reset_fast_presses(); // exit this mode after one use
  275.  
  276.             toggle_options((options ^ 0b00000001), 1); // memory
  277.             toggle_options((options ^ 0b00000010), 2); // stop at the top
  278.             toggle_options((options ^ 0b00000100), 3); // turbo timer
  279.             toggle_options(DEFAULTS, 4); // reset to defaults
  280.         }
  281.         else if (fast_presses[0] == 3) {
  282.         //else if (output == BATT_CHECK) {
  283.              blink(battcheck(), BLINK_SPEED/4);
  284.              _delay_s(); _delay_s();
  285.             // shouldn't need to worry about resetting fast_presses here, but may need to later
  286.         }
  287.     else if ( !ramp_stopped ) {
  288.             set_level(output);
  289.             if(output == 1 || output == RAMP_SIZE) { _delay_s(); }  // pause for a second at the lowest & highest levels
  290.            
  291.             if( (ramp_direction == RAMPING_DOWN && output == 1) || (ramp_direction == RAMPING_UP && output == RAMP_SIZE) ) {
  292.                 ramp_direction = !ramp_direction;
  293.             }
  294.            
  295.             if( output == RAMP_SIZE && stop_at_the_top() ) {
  296.                 ramp_stopped = 1;
  297.             }
  298.            
  299.             if(ramp_direction == RAMPING_UP) { output++; }
  300.             else { output--; }
  301.            
  302.             _delay_ms(RAMP_TIME*1000/RAMP_SIZE);
  303.            
  304.             // if we've been ramping around for more than half a second, reset the fast_presses
  305.             ticks = ticks + (RAMP_TIME*1000/RAMP_SIZE);
  306.             if(ticks > 500) {
  307.                 ticks = 0;
  308.                 reset_fast_presses();
  309.             }
  310.            
  311.     }
  312.         else {
  313.             if ((output >= TURBO_THRESHOLD) && ( ttimer_is_enabled() ) && (ticks > (TURBO_MINUTES * TICKS_PER_MINUTE))) {
  314.                 set_pwm(TURBO_LOWER);
  315.             }
  316.       else {
  317.         ticks ++; // count ticks for turbo timer
  318.                 set_level(output);
  319.       }
  320.      
  321.             _delay_ms(500);  // Otherwise, just sleep.
  322.       reset_fast_presses();
  323.         }
  324. #ifdef VOLTAGE_MON
  325.         if (ADCSRA & (1 << ADIF)) {  // if a voltage reading is ready
  326.             voltage = ADCH;  // get the waiting value
  327.    
  328.             if (voltage < ADC_LOW) { // See if voltage is lower than what we were looking for
  329.                 lowbatt_cnt ++;
  330.             } else {
  331.                 lowbatt_cnt = 0;
  332.             }
  333.            
  334.             if (lowbatt_cnt >= 8) {  // See if it's been low for a while, and maybe step down
  335.                 if (output > 1) {  // not yet at the lowest level
  336.                     output = (output>>1); // divide output in half using bitwise operators
  337.                 } else { // Can't go any lower
  338.                     set_pwm(0); // Turn off the light
  339.                     set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Power down as many components as possible
  340.                     sleep_mode();
  341.                 }
  342.                 set_level(output);
  343.                 lowbatt_cnt = 0;
  344.                 _delay_s(); // Wait before lowering the level again
  345.             }
  346.  
  347.             ADCSRA |= (1 << ADSC); // Make sure conversion is running for next time through
  348.         }
  349. #endif  // ifdef VOLTAGE_MON
  350.     }
  351. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement