/** * High voltage source control logic */ #include "high_voltage.h" #include "iopins.h" #include "global_state.h" #include #include #include static volatile bool is_pwm_on = false; // tube starting voltage is 350V #define TURN_ON_VOLTAGE 360.0 #define TURN_OFF_VOLTAGE 390.0 #define MINIMAL_VOLTAGE 350.0 // sensing via resistive divider #define VREF 1.1 // in kiloohms #define R_UPPER 10000.0 #define R_LOWER 27.0 #define ADC_MAXVAL 1023 #define RDIV_FACTOR (R_LOWER / (R_UPPER + R_LOWER)) /// ADC input voltage that's too low - PWM must start #define VSEN_TURN_ON (TURN_ON_VOLTAGE * RDIV_FACTOR) /// ADC input voltage that's too high - PWM must stop #define VSEN_TURN_OFF (TURN_OFF_VOLTAGE * RDIV_FACTOR) /// ADC input voltage that's lowest possible for the geiger tube to function #define VSEN_MINIMAL (MINIMAL_VOLTAGE * RDIV_FACTOR) // the above, but converted to ADC word #define ADCVAL_TURN_ON ((uint16_t) (ADC_MAXVAL * (VSEN_TURN_ON / VREF))) #define ADCVAL_TURN_OFF ((uint16_t) (ADC_MAXVAL * (VSEN_TURN_OFF / VREF))) #define ADCVAL_MINIMAL ((uint16_t) (ADC_MAXVAL * (VSEN_MINIMAL / VREF))) /// Channel used to measure VSEN #define VSEN_ADC_CHANNEL 0 /// when capacitor was last fully charged static volatile uint16_t ts_cap_charged = 0; static void pwm_on() { TCCR0A |= _BV(COM0B1); is_pwm_on = true; } static void pwm_off() { TCCR0A &= ~_BV(COM0B1); is_pwm_on = false; } ISR(ADC_vect) { // Service the boost converter uint16_t analog = ADCW; if (is_pwm_on) { if (analog >= ADCVAL_TURN_OFF) { pwm_off(); ts_cap_charged = timestamp_100ms; } else { // If fail to reach target voltage in reasonable time, // show weak battery icon and stop trying. if ((timestamp_100ms - ts_cap_charged) > 10 && analog < ADCVAL_MINIMAL) { is_weak_battery = true; } } } else if (analog < ADCVAL_TURN_ON) { pwm_on(); } } static void init_pwm_out() { // Output is OC0A as_output(D5); // initialize the timer // Fast PWM mode, Output to OC0A // clock is 16MHz, presc /64, counting to 80 -> freq 3125Hz // Duty cycle = appx. 60% OCR0A = 80; OCR0B = 46; TCCR0A = _BV(WGM00) | _BV(WGM01); TCCR0B = _BV(CS01) | _BV(CS00) | _BV(WGM02); } static void init_adc() { ADCSRA = // 128 prescaler -> 125 kHz _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0) // auto trigger | _BV(ADATE) // interrupt enable | _BV(ADIE); ADCSRB = 0; // free-running DIDR0 = ADC0D; // disable the digital input buffer on the pin ADMUX = // internal ref 1.1V _BV(REFS0) | _BV(REFS1) // select channel | VSEN_ADC_CHANNEL; sbi(ADCSRA, ADEN); // Enable ADC sbi(ADCSRA, ADSC); // Start } void init_high_voltage() { init_pwm_out(); init_adc(); } void hv_disable() { ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { // if ADC is disabled, the IRQ never fires and it won't be turned on cbi(ADCSRA, ADEN); // Turn it off pwm_off(); } }