Advertisement
Guest User

tiny25/45/85 Vcc and temperature reading

a guest
Sep 13th, 2016
429
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 14.65 KB | None | 0 0
  1. /* Compiled size: 1574 bytes
  2.  *
  3.  * Firmware for AtTiny25-based flashlight drivers with a clicky switch, DD FET and 1x7135
  4.  * Features:
  5.  * - any number of solid modes, cell voltage check, mcu temperature check
  6.  * - visibility strobe, beacons, motion-freeze strobes
  7.  * - does not use OTC, no memory, always start in programmed mode, a quick off-click steps through the modes
  8.  * - double-click hidden mode accessible from start mode
  9.  * - phase-correct PWM at 1961 Hz, compromise to accomodate 7135 switching frequency, audible noise and flicker
  10.  * - step light down at low voltage, switch off at critical voltage
  11.  * - optional TURBO mode time-out, optional temperature step down
  12.  *
  13.  * Based on original work by JonnyC and ToyKeeper / Selene Scriven
  14.  *
  15.  * ATTINY25 circuit:
  16.  *           ---
  17.  * PB5:RST -| o |- VCC
  18.  * PB3:OTC -|   |- PB2:ADC1
  19.  * PB4:    -|   |- PB1:PWM1 FET
  20.  *     GND -|   |- PB0:PWM0 1x7135
  21.  *           ---
  22.  * ADC4 : Temperature
  23.  *
  24.  * Fuses:
  25.  *      Low:  0x62      (8 MHz clock, 8* divider, 14 CK + 64 ms startup)
  26.  *      High: 0xdf      (no BOD, SPI enabled)
  27.  *      Ext:  0xff      (self prog disabled)
  28. */
  29.  
  30. #define F_CPU 1000000UL
  31.  
  32. /* ===============================================================================================================
  33.  * Settings to customise driver. Also patch modes[] below to match.
  34.  */
  35. #define D1                      2                           // Drop over reverse polarity protection diode = 0.2 V
  36. #define BATT_LOW                32                          // Cell voltage to step light down = 3.2 V
  37. #define BATT_CRIT               28                          // Cell voltage to shut the light off = 2.8 V
  38. #define SOLID_MODES             3                           // 0-3:     solid modes
  39. #define BATT_VOLT_MODE          4                           // 4:       diagnostics, cell voltage
  40. #define MCU_TEMP_MODE           5                           // 5:       diagnostics, mcu temperature
  41. #define HI_VIS_MODE             6                           // 6:       high-visibility strobe mode
  42. #define BEACON1_MODE            7                           // 7:       slow strobe beacon mode
  43. #define BEACON2_MODE            8                           // 8:       lighthouse beacon mode
  44. #define BEACON3_MODE            9                           // 9:       SOS beacon mode
  45. #define FIXED_STROBE_MODES      13                          // 10-13:   fixed-speed motion-freeze strobe modes
  46. #define LAST_MODE               13                          // wrap around from here
  47. #define START_MODE              1                           // after long press
  48. #define TURBO_MODE              0                           // one of the solid modes
  49. #define TURBO_TIMEOUT           15                          // x2 seconds to stay in turbo mode - comment out to disable
  50. #define TEMPOUT                 55                          // temperature to step the light down - comment out to disable
  51. #define STEPDOWN_MODE           1                           // mode after step-down
  52. #define HIDDEN_MODE             TURBO_MODE                  // activated with double-click from OFF
  53. #define COUNTS_100US            25                          // _delay_loop_2 counts to get 100 us (1 count = 4 CLKs)
  54. #define DOUBLE_CLICK_TIMEOUT    4                           // double-click time-outs (64 ms clicks)
  55.  
  56. /* ===============================================================================================================
  57.  */
  58.  
  59. #include <util/delay_basic.h>
  60. #include <avr/pgmspace.h>
  61. #include <avr/interrupt.h>
  62. #include <avr/wdt.h>   
  63. #include <avr/sleep.h>
  64. #define PWM0_PIN                PB0                         // port B, pin 0, control signal for 7135
  65. #define PWM1_PIN                PB1                         // port B, pin 1, control signal for FET
  66. #define PWM0_LVL                OCR0A                       // output compare register for 7135
  67. #define PWM1_LVL                OCR0B                       // output compare register for FET
  68. #define ADCMUX_TEMP             0b10001111                  // ADCMUX register setup to read temperature
  69. #define ADCMUX_VCC              0b00001100                  // ADCMUX register setup to read Vbg referenced to Vcc
  70.  
  71. /* ===============================================================================================================
  72.  * Global variables
  73.  */
  74. // Levels for 7135
  75. PROGMEM const uint8_t levels0[] = {            
  76. // log ramp for 4 A DD
  77.     0,2,2,3,3,4,4,5,5,6,7,8,9,10,11,12,14,15,17,19,21,24,27,30,34,38,43,48,54,60,67,75,85,95,106,119,133,150,168,188,211,236,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
  78. // log ramp for 10 A DD
  79. //  0,2,3,3,4,4,5,5,6,7,8,9,10,12,13,15,17,20,22,25,29,33,37,42,48,55,62,71,80,91,104,118,135,153,174,198,225,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
  80. };
  81. // Levels for FET
  82. PROGMEM const uint8_t levels1[] = {                    
  83. // log ramp for 4 A DD
  84.     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,7,11,16,20,26,32,39,47,55,65,76,88,101,116,133,153,174,198,225,255
  85. // log ramp for 10 A DD
  86. //  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,6,8,11,14,17,20,24,29,34,40,47,55,64,74,85,98,113,130,149,170,195,223,255
  87. };
  88. // Modes
  89. const uint8_t modes[] = {                      
  90.     63, 42, 29, 1,                                          // solid modes level no
  91.     0,                                                      // reserved, diagnostics mode 1
  92.     0,                                                      // reserved, diagnostics mode 2
  93.     0,                                                      // reserved, high-visibility strobe
  94.     0,                                                      // reserved, beacon 1
  95.     0,                                                      // reserved, beacon 2
  96.     0,                                                      // reserved, beacon 3  
  97.     9, 4, 1, 0                                              // *10ms delay strobe modes, 2nd part. 1st part = 10 ms fixed
  98. };
  99.  
  100. // Keep track of cell voltage in ISRs, 10-bit resolution required for impedance check
  101. volatile int16_t voltage = 0;                               // in volts * 10
  102. volatile int16_t temperature = 0;                           // in degC
  103. volatile uint8_t adc_step = 0;                              // steps 0, 1 read voltage, steps 2, 3 reads temperature - only use steps 1, 3 values
  104. // Store mode in uninitialized memory so it will not be overwritten and
  105. // can still be read at startup after short (<500ms) power-off decay.
  106. // Used to tell if user did a short press, long press or double-press.
  107. volatile uint8_t noinit_decay __attribute__ ((section (".noinit")));
  108. volatile uint8_t noinit_mode __attribute__ ((section (".noinit")));
  109. volatile uint8_t noinit_prev_on_time __attribute__ ((section (".noinit")));
  110. volatile uint8_t mode_idx;
  111.  
  112. /* ===============================================================================================================
  113.  * Subroutines
  114.  */
  115.  
  116. static void _delay_100us(uint8_t n) {                       // 0.1 ms to 25.5 ms timer
  117.     do {_delay_loop_2(COUNTS_100US);} while (--n);          // cost is 12 bytes per call
  118. }
  119.  
  120. static void _delay_10ms(uint8_t n){                         // 10 ms to 2.55 s timer   
  121.     while (n--) {_delay_100us(100);}                        // use this one when possible, cost is 2 bytes/call
  122. }                                                           // less efficient while loop, to allow 0 us time delay
  123.  
  124. static void _delay_5s() {                                   // wait 5 s
  125.     _delay_10ms(250);
  126.     _delay_10ms(250);
  127. }
  128.  
  129. void flash(uint8_t n) {                                     // flash once at full 7135 power
  130.     PWM0_LVL = 255;
  131.     _delay_10ms(n);                                         // ON for n*10 ms
  132.     PWM0_LVL =   0;                
  133.     _delay_10ms(n<<2);                                      // OFF for n*40 ms, keep n below 64
  134. }
  135.  
  136. /* ===============================================================================================================
  137.  * 64 ms interrupt
  138.  */
  139.  
  140. void WDT_on()                                               // Setup watchdog timer to interrupt every x ms
  141. {
  142.     cli();                                                  // disable interrupts
  143.     wdt_reset();                                            // reset the WDT
  144.     WDTCR |= (1<<WDCE) | (1<<WDE);                          // start timed sequence
  145.     WDTCR = (1<<WDIE) | (1<<WDP1);                          // enable interrupt every 64 ms
  146.     sei();                                                  // enable interrupts
  147. }
  148.  
  149. void WDT_off()                                              // Turn off watchdog timer
  150. {
  151.     cli();                                                  // disable interrupts
  152.     wdt_reset();                                            // reset the WDT
  153.     MCUSR &= ~(1<<WDRF);                                    // clear Watchdog reset flag
  154.     WDTCR |= (1<<WDCE) | (1<<WDE);                          // start timed sequence
  155.     WDTCR = 0x00;                                           // disable WDT
  156.     sei();                                                  // enable interrupts
  157. }
  158.  
  159. ISR(ADC_vect)                                               // ADC done interrupt service routine
  160. {
  161.     int16_t adcin = ADC;
  162.     if (adc_step == 1) {                                    // ignore first ADC value from step 0
  163.         // Read cell voltage
  164.         adcin = 11264/adcin + D1;                           // in volts * 10: 10 * 1.1 * 1024 / ADC + D1
  165.         if (voltage > 0) {
  166.             if (voltage < adcin) {++voltage;}               // crude low pass filter
  167.             if (voltage > adcin) {--voltage;}
  168.         } else {voltage = adcin;}                           // prime on first read
  169.     }
  170.     if (adc_step == 3) {                                    // ignore first ADC value from step 2
  171.         // Read MCU temperature
  172.         adcin -= 275;                                       // 300 = 25 degC
  173.         if (temperature > 0) {
  174.             if (temperature < adcin) {++temperature;}       // crude low pass filter
  175.             if (temperature > adcin) {--temperature;}
  176.         } else {temperature = adcin;}                       // prime on first read
  177.     }
  178.     adc_step++; if (adc_step > 3) {adc_step = 0;}
  179.     if (adc_step < 2) {                                     // steps 0, 1 read voltage, steps 2, 3 read temperature
  180.         ADMUX  = ADCMUX_VCC;}
  181.     else {
  182.         ADMUX  = ADCMUX_TEMP;}
  183. }
  184.  
  185. ISR(WDT_vect)                                               // WDT interrupt service routine
  186. {                                              
  187.     // Double click timeout
  188.     if (noinit_prev_on_time < DOUBLE_CLICK_TIMEOUT) {  
  189.                 ++noinit_prev_on_time;
  190.     }
  191.     // start next ADC conversion and arm interrupt
  192.     ADCSRA |= (1 << ADSC) | (1 << ADIE);
  193. }
  194.  
  195. /* ===============================================================================================================
  196.  * Main
  197.  */
  198. int main(void)
  199. {
  200.     // Determine what mode we should fire up, read last mode that was saved
  201.     if (noinit_decay) {                                     // long click, reset to start mode
  202.         mode_idx = START_MODE;
  203.         noinit_prev_on_time = 0;                            // enable double click detection on initial start
  204.     } else if (noinit_prev_on_time >= DOUBLE_CLICK_TIMEOUT) {   // short click, or first click of double click
  205.         mode_idx = noinit_mode + 1;
  206.         if (mode_idx > LAST_MODE) {mode_idx = START_MODE;} 
  207.     } else {mode_idx = HIDDEN_MODE;}                        // second click of double-click, go to hidden mode
  208.     noinit_decay = 0;                                       // set noinit data for next boot
  209.     noinit_mode = mode_idx;
  210.     // Turn ADC on (13 CLKs required for conversion, go max 200 kHz for 10-bit resolution)
  211.     ADMUX  = ADCMUX_VCC;                                    // 1.1 V reference, not left-adjust, Vbg
  212.     DIDR0 |= (1 << ADC1D);                                  // disable digital input on ADC1 pin to reduce power consumption
  213.     ACSR  |= (1 << ACD);                                    // analog comparator off to save power
  214.     ADCSRA = (1 << ADEN ) | (1 << ADSC ) | 0b100;           // enable, start, ADC clock prescale = 16 (62.5 kHz)
  215.     // Start interrupt service routine
  216.     WDT_on();
  217.     // Setup PWM pins for 1960 Hz, phase-correct. F_pwm = F_clkio / N / 510, N = precale factor
  218.     DDRB = (1<<PWM1_PIN) | (1<<PWM0_PIN);                   // enable PWM1, PWM0 control output pins
  219.     if (mode_idx <= (FIXED_STROBE_MODES-4)) {               // set for PWM output, otherwise regular output (needed for fast strobes)
  220.         TCCR0B  = (1 << CS00);                              // pre-scaler for timer = 1
  221.         TCCR0A  = (1 << WGM00);                             // PWM mode 1 (phase correct, TOP=xFF)
  222.         TCCR0A |= (1 << COM0A1);                            // PWM0: Normal direction
  223.         TCCR0A |= (1 << COM0B1);                            // PWM1: Normal direction
  224.     }
  225.  
  226.     uint8_t i, j;
  227.     uint8_t turbo_cnt = 0;
  228.     PWM0_LVL = 0;
  229.     PWM1_LVL = 0;
  230.     // Main loop
  231.     while(1) {
  232.         // Manage modes
  233.         // =======================================================================================================
  234.         if (mode_idx <= SOLID_MODES) {                      // ======= SOLID MODES ===============================
  235.             PWM0_LVL = pgm_read_byte(levels0 + modes[mode_idx]);
  236.             PWM1_LVL = pgm_read_byte(levels1 + modes[mode_idx]);
  237. #ifdef TURBO_TIMEOUT           
  238.             if (mode_idx == TURBO_MODE) {                   // TURBO MODE time-out
  239.                 turbo_cnt++;
  240.                 _delay_10ms(200);                           // turbo_cnt in 2-second steps
  241.             } else {turbo_cnt = 0;}
  242.             if (turbo_cnt >= TURBO_TIMEOUT) {               // step down from TURBO
  243.                 mode_idx = STEPDOWN_MODE;
  244.                 noinit_mode = mode_idx;
  245.             }
  246. #endif
  247. #ifdef TEMPOUT         
  248.             if ((temperature > 0) && (mode_idx < STEPDOWN_MODE) && (temperature >= TEMPOUT )) { // step down for heat
  249.                 mode_idx = STEPDOWN_MODE;
  250.                 noinit_mode = mode_idx;
  251.             }  
  252. #endif         
  253.         }
  254.         else if (mode_idx == BATT_VOLT_MODE) {              // ======= CELL VOLTAGE CHECK MODE ========================
  255.             if (voltage > 0) {
  256.                 j = voltage;
  257.                 i=10; while (j >= i) {                      // flash the 10's
  258.                     flash(8);
  259.                     i += 10;
  260.                 }
  261.                 _delay_10ms(62);
  262.                 i -= 10;
  263.                 j -= i;
  264.                 while (j) {                                 // flash the 1's
  265.                     flash(8);
  266.                     j--;
  267.                 }
  268.                 _delay_10ms(250);
  269.             }
  270.         }
  271.         else if (mode_idx == MCU_TEMP_MODE) {               // ======= MCU TEMPERATURE CHECK MODE ========================
  272.             if (temperature > 0) {
  273.                 j = temperature;
  274.                 i=10; while (j >= i) {                      // flash the 10's
  275.                     flash(8);
  276.                     i += 10;
  277.                 }
  278.                 _delay_10ms(62);
  279.                 i -= 10;
  280.                 j -= i;
  281.                 while (j) {                                 // flash the 1's
  282.                     flash(8);
  283.                     j--;
  284.                 }
  285.                 _delay_10ms(250);
  286.             }
  287.         }
  288.         else if (mode_idx <= HI_VIS_MODE) {                 // ======= HI-VISIBILITY MODE ========================
  289.             PWM0_LVL = 127;                                 // background light
  290.             i=4; do {                                       // flash brighter  
  291.                 PWM1_LVL = 255;            
  292.                 _delay_10ms(1);
  293.                 PWM1_LVL = 0;
  294.                 _delay_10ms(6);
  295.             } while (--i);
  296.             _delay_10ms(72);
  297.         }
  298.         else if (mode_idx == BEACON3_MODE) {                // ======= SOS BEACON MODE =======================
  299.         #define MORSE_T 10
  300.             i = 3; do {
  301.                 PWM1_LVL = 255;
  302.                 _delay_10ms(MORSE_T);
  303.                 PWM1_LVL = 0;
  304.                 _delay_10ms(MORSE_T);
  305.             } while (--i);
  306.             i = 3; do {
  307.                 PWM1_LVL = 255;
  308.                 _delay_10ms(MORSE_T*3);
  309.                 PWM1_LVL = 0;
  310.                 _delay_10ms(MORSE_T);
  311.             } while (--i);
  312.             i = 3; do {
  313.                 PWM1_LVL = 255;
  314.                 _delay_10ms(MORSE_T);
  315.                 PWM1_LVL = 0;
  316.                 _delay_10ms(MORSE_T);
  317.             } while (--i);
  318.             _delay_5s();
  319.         }
  320.         else if (mode_idx == BEACON2_MODE) {                // ======= LIGHTHOUSE BEACON MODE =======================
  321.             i = 0; do {
  322.             i++;
  323.             PWM0_LVL = pgm_read_byte(levels0 + i);
  324.             PWM1_LVL = pgm_read_byte(levels1 + i);
  325.             _delay_10ms(1);
  326.             } while (i < 63);
  327.             do {
  328.             PWM0_LVL = pgm_read_byte(levels0 + i);
  329.             PWM1_LVL = pgm_read_byte(levels1 + i);
  330.             _delay_10ms(1);
  331.             } while (i--);
  332.             _delay_10ms(80);
  333.         }
  334.         else if (mode_idx == BEACON1_MODE) {                // ======= FLASH BEACON MODE =======================
  335.             PWM1_LVL = 255;
  336.             _delay_10ms(10);
  337.             PWM1_LVL = 0;
  338.             _delay_5s();
  339.         }
  340.         else if (mode_idx <= FIXED_STROBE_MODES) {          // ======= FIXED FREQUENCY STROBE MODES ==============
  341.             PORTB = (1 << PWM1_PIN);
  342.             _delay_100us(1);
  343.             PORTB = 0;
  344.             _delay_100us(99);                               // 10 ms part of delay, allow 100 us for strobe on
  345.             _delay_10ms(modes[mode_idx]);                   // x10 ms part of delay
  346.         }      
  347.         // Battery protection
  348.         // =======================================================================================================
  349.         if ((voltage > 0) && (voltage <= BATT_LOW) && (mode_idx < 2) ) {    // ignore blinkies and low power modes
  350.             ++mode_idx;                                     // step power down one level
  351.             noinit_mode = mode_idx;
  352.             _delay_10ms(200);                               // dont step down too fast
  353.         }
  354.         if ((voltage > 0) && (voltage <= BATT_CRIT)) {      // flash and switch off
  355.             PWM1_LVL = 0;
  356.             i = 6; do {
  357.                 flash(1);
  358.             } while (--i);
  359.             WDT_off();                                      // interrupts off
  360.             ADCSRA &= ~(1 << ADEN);                         // ADC off
  361.             _delay_10ms(1);                                 // make sure PWM is 0
  362.             set_sleep_mode(SLEEP_MODE_PWR_DOWN);            // power down as many components as possible
  363.             sleep_mode();
  364.         }
  365.     }
  366. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement