You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
atmega-geiger/src/high_voltage.c

140 lines
3.2 KiB

/**
* High voltage source control logic
*/
#include "high_voltage.h"
#include "iopins.h"
#include "global_state.h"
#include <stdbool.h>
#include <avr/interrupt.h>
#include <util/atomic.h>
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) {
status_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();
}
}