|
|
|
@ -1,229 +1,39 @@ |
|
|
|
|
#include "twi.h" |
|
|
|
|
#include "ssd1306.h" |
|
|
|
|
#include "sevenseg.h" |
|
|
|
|
#include "adc.h" |
|
|
|
|
#include "iopins.h" |
|
|
|
|
#include <avr/interrupt.h> |
|
|
|
|
#include <util/atomic.h> |
|
|
|
|
#include <util/delay.h> |
|
|
|
|
#include <avr/sleep.h> |
|
|
|
|
#include "time_base.h" |
|
|
|
|
#include "high_voltage.h" |
|
|
|
|
#include "radiation.h" |
|
|
|
|
#include "user_interface.h" |
|
|
|
|
|
|
|
|
|
static void show_loading_icon(); |
|
|
|
|
static void show_empty_battery_icon(); |
|
|
|
|
static void shutdown_due_to_weak_battery(); |
|
|
|
|
static void show_current_radiation(); |
|
|
|
|
|
|
|
|
|
static void second_callback_irq(); |
|
|
|
|
|
|
|
|
|
// tube starting voltage is 350V
|
|
|
|
|
#define TURN_ON_VOLTAGE 355.0 |
|
|
|
|
#define TURN_OFF_VOLTAGE 380.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 |
|
|
|
|
|
|
|
|
|
static struct SevenSeg sseg = { |
|
|
|
|
.x0 = 0, |
|
|
|
|
.y0 = 0, |
|
|
|
|
.charwidth = 17, |
|
|
|
|
.thick = 3, |
|
|
|
|
//.charwidth = 16,
|
|
|
|
|
//.thick = 1,
|
|
|
|
|
.spacing = 4, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static volatile bool is_pwm_on = false; |
|
|
|
|
static volatile bool req_update_display = false; |
|
|
|
|
static volatile bool is_weak_battery = false; |
|
|
|
|
|
|
|
|
|
#define MEAS_BIN_COUNT 120 |
|
|
|
|
typedef uint8_t meas_bin_t; |
|
|
|
|
#define MEAS_MAX_COUNT 255 |
|
|
|
|
|
|
|
|
|
/// measurements in the last X seconds
|
|
|
|
|
static volatile meas_bin_t measurements[MEAS_BIN_COUNT]; |
|
|
|
|
/// Currently active measurement bin, increments once per second
|
|
|
|
|
static volatile uint8_t meas_bin = 0; |
|
|
|
|
|
|
|
|
|
/// timestamp counted in units of 100ms
|
|
|
|
|
static volatile uint16_t timestamp_100ms = 0; |
|
|
|
|
/// sub-second counter, counts 0-9 then overflows
|
|
|
|
|
static volatile uint16_t subsec_counter = 0; |
|
|
|
|
|
|
|
|
|
// when capacitor was last fully charged
|
|
|
|
|
static volatile uint16_t ts_cap_charged = 0; |
|
|
|
|
|
|
|
|
|
static inline void pwm_on() { |
|
|
|
|
TCCR0A |= _BV(COM0B1); |
|
|
|
|
is_pwm_on = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void inline pwm_off() { |
|
|
|
|
TCCR0A &= ~_BV(COM0B1); |
|
|
|
|
is_pwm_on = false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// geiger tube pin change interrupt
|
|
|
|
|
ISR(INT0_vect) |
|
|
|
|
{ |
|
|
|
|
if (measurements[meas_bin] < MEAS_MAX_COUNT) { |
|
|
|
|
measurements[meas_bin]++; |
|
|
|
|
} |
|
|
|
|
req_update_display = true; |
|
|
|
|
} |
|
|
|
|
/* globals */ |
|
|
|
|
volatile bool req_update_display = false; |
|
|
|
|
volatile bool is_weak_battery = false; |
|
|
|
|
|
|
|
|
|
// 1 second counter
|
|
|
|
|
ISR(TIMER1_COMPA_vect) |
|
|
|
|
{ |
|
|
|
|
timestamp_100ms++; |
|
|
|
|
static void shutdown_due_to_weak_battery(); |
|
|
|
|
|
|
|
|
|
subsec_counter++; |
|
|
|
|
if (subsec_counter == 10) { |
|
|
|
|
subsec_counter = 0; |
|
|
|
|
second_callback_irq(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void second_callback_irq() |
|
|
|
|
void __attribute__((noreturn)) main() |
|
|
|
|
{ |
|
|
|
|
meas_bin++; |
|
|
|
|
if (meas_bin >= MEAS_BIN_COUNT) { |
|
|
|
|
meas_bin = 0; |
|
|
|
|
} |
|
|
|
|
measurements[meas_bin] = 0; |
|
|
|
|
req_update_display = true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
init_user_interface(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void init_pcisr() { |
|
|
|
|
as_input(D2); |
|
|
|
|
|
|
|
|
|
// using INT0 - arduino pin D2
|
|
|
|
|
EICRA = _BV(ISC01) | _BV(ISC00); // rising edge
|
|
|
|
|
EIMSK = _BV(INT0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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_second_base() { |
|
|
|
|
// CTC & presc=256
|
|
|
|
|
TCCR1B = (1 << WGM12) | (1 << CS12); |
|
|
|
|
TIMSK1 = (1 << OCIE1A); // Enable CTC interrupt
|
|
|
|
|
OCR1A = 6250; // 100ms
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void vsen_adc_init() { |
|
|
|
|
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 __attribute__((noreturn)) main() { |
|
|
|
|
// clear the measurement buffer
|
|
|
|
|
memset((void*) measurements, 0, sizeof(measurements)); |
|
|
|
|
|
|
|
|
|
TWI_Init(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
init_second_base(); |
|
|
|
|
|
|
|
|
|
init_pwm_out(); |
|
|
|
|
|
|
|
|
|
ssd1306_128x32_i2c_init(); |
|
|
|
|
ssd1306_clearScreen(); |
|
|
|
|
|
|
|
|
|
// TODO use ADC interrupt
|
|
|
|
|
|
|
|
|
|
init_pcisr(); |
|
|
|
|
|
|
|
|
|
// TODO show loading icon (can't show anything useful at least until the first tick arrives)
|
|
|
|
|
show_loading_icon(); |
|
|
|
|
init_timebase(); |
|
|
|
|
init_radiation(); |
|
|
|
|
|
|
|
|
|
// --- let's go ---
|
|
|
|
|
sei(); |
|
|
|
|
|
|
|
|
|
vsen_adc_init(); |
|
|
|
|
// this needs interrupts enabled
|
|
|
|
|
init_high_voltage(); |
|
|
|
|
|
|
|
|
|
for (;;) { |
|
|
|
|
_delay_ms(100); |
|
|
|
|
|
|
|
|
|
// check if redraw is needed
|
|
|
|
|
bool update_display = false; |
|
|
|
|
ATOMIC_BLOCK(ATOMIC_FORCEON) { |
|
|
|
|
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { |
|
|
|
|
update_display = req_update_display; |
|
|
|
|
req_update_display = false; |
|
|
|
|
} |
|
|
|
@ -238,37 +48,18 @@ void __attribute__((noreturn)) main() { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void show_current_radiation() |
|
|
|
|
{ |
|
|
|
|
// TODO
|
|
|
|
|
sseg_number(&sseg, timestamp_100ms, 5, 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void __attribute__((noreturn)) shutdown_due_to_weak_battery() |
|
|
|
|
{ |
|
|
|
|
pwm_off(); |
|
|
|
|
|
|
|
|
|
show_empty_battery_icon(); |
|
|
|
|
|
|
|
|
|
cli(); |
|
|
|
|
hv_disable(); |
|
|
|
|
|
|
|
|
|
show_empty_battery_icon(); |
|
|
|
|
// try to preserve power; but the display will still drain the battery.
|
|
|
|
|
// we should probably shut it off too.
|
|
|
|
|
|
|
|
|
|
for(;;) { |
|
|
|
|
for (;;) { |
|
|
|
|
sleep_enable(); |
|
|
|
|
sleep_cpu(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void show_empty_battery_icon() |
|
|
|
|
{ |
|
|
|
|
// TODO
|
|
|
|
|
sseg_number(&sseg, 9999, 5, 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void show_loading_icon() |
|
|
|
|
{ |
|
|
|
|
// TODO
|
|
|
|
|
sseg_number(&sseg, 0, 5, 0); |
|
|
|
|
} |
|
|
|
|