add systick and adc interrupt
This commit is contained in:
+217
-73
@@ -4,24 +4,142 @@
|
||||
#include "adc.h"
|
||||
#include "iopins.h"
|
||||
#include <avr/interrupt.h>
|
||||
#include <util/atomic.h>
|
||||
|
||||
const uint16_t adc_target16_acceptable = 900;
|
||||
const uint16_t adc_target16 = 972;
|
||||
//const uint8_t adc_target8 = 243;
|
||||
static void show_loading_icon();
|
||||
static void show_empty_battery_icon();
|
||||
static void shutdown_due_to_weak_battery();
|
||||
static void show_current_radiation();
|
||||
|
||||
static bool g_pwm_on = false;
|
||||
static void second_callback_irq();
|
||||
|
||||
static volatile uint16_t tick_counter = 0;
|
||||
static volatile bool tick_counter_changed = false;
|
||||
// tube starting voltage is 350V
|
||||
#define TURN_ON_VOLTAGE 355.0
|
||||
#define TURN_OFF_VOLTAGE 380.0
|
||||
#define MINIMAL_VOLTAGE 350.0
|
||||
|
||||
// Tick
|
||||
ISR(INT0_vect)
|
||||
{
|
||||
tick_counter++;
|
||||
tick_counter_changed = true;
|
||||
// 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 init_isr() {
|
||||
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;
|
||||
}
|
||||
|
||||
// 1 second counter
|
||||
ISR(TIMER1_COMPA_vect)
|
||||
{
|
||||
timestamp_100ms++;
|
||||
|
||||
subsec_counter++;
|
||||
if (subsec_counter == 10) {
|
||||
subsec_counter = 0;
|
||||
second_callback_irq();
|
||||
}
|
||||
}
|
||||
|
||||
static void second_callback_irq()
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void init_pcisr() {
|
||||
as_input(D2);
|
||||
|
||||
// using INT0 - arduino pin D2
|
||||
@@ -45,86 +163,112 @@ static void init_pwm_out() {
|
||||
TCCR0B = _BV(CS01) | _BV(CS00) | _BV(WGM02);
|
||||
}
|
||||
|
||||
static void pwm_on() {
|
||||
TCCR0A |= _BV(COM0B1);
|
||||
g_pwm_on = true;
|
||||
static void init_second_base() {
|
||||
// CTC & presc=256
|
||||
TCCR1B = (1 << WGM12) | (1 << CS12);
|
||||
TIMSK1 = (1 << OCIE1A); // Enable CTC interrupt
|
||||
OCR1A = 6250; // 100ms
|
||||
}
|
||||
|
||||
static void pwm_off() {
|
||||
TCCR0A &= ~_BV(COM0B1);
|
||||
g_pwm_on = false;
|
||||
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 main() {
|
||||
void __attribute__((noreturn)) main() {
|
||||
// clear the measurement buffer
|
||||
memset((void*) measurements, 0, sizeof(measurements));
|
||||
|
||||
TWI_Init();
|
||||
adc_init();
|
||||
|
||||
|
||||
init_second_base();
|
||||
|
||||
init_pwm_out();
|
||||
|
||||
ssd1306_128x32_i2c_init();
|
||||
ssd1306_clearScreen();
|
||||
|
||||
struct SevenSeg sseg = {
|
||||
.x0 = 0,
|
||||
.y0 = 0,
|
||||
.charwidth = 17,
|
||||
.thick = 3,
|
||||
//.charwidth = 16,
|
||||
//.thick = 1,
|
||||
.spacing = 4,
|
||||
};
|
||||
// TODO use ADC interrupt
|
||||
|
||||
init_isr();
|
||||
init_pcisr();
|
||||
|
||||
// TODO show loading icon (can't show anything useful at least until the first tick arrives)
|
||||
show_loading_icon();
|
||||
|
||||
// --- let's go ---
|
||||
sei();
|
||||
|
||||
// TODO show loading icon
|
||||
sseg_number(&sseg, 0, 5, 0);
|
||||
vsen_adc_init();
|
||||
|
||||
// request ADC meas
|
||||
adc_async_start_measure_word(0);
|
||||
|
||||
// uint16_t cnt = 0;
|
||||
uint16_t analog;
|
||||
uint16_t count_boost_fail = 0;
|
||||
for (;;) {
|
||||
if (adc_async_ready()) {
|
||||
analog = adc_async_get_result_word();
|
||||
adc_async_start_measure_word(0);
|
||||
_delay_ms(100);
|
||||
|
||||
bool good_voltage = analog >= adc_target16;
|
||||
// check if redraw is needed
|
||||
bool update_display = false;
|
||||
ATOMIC_BLOCK(ATOMIC_FORCEON) {
|
||||
update_display = req_update_display;
|
||||
req_update_display = false;
|
||||
}
|
||||
|
||||
if (g_pwm_on) {
|
||||
if (good_voltage) {
|
||||
pwm_off();
|
||||
count_boost_fail = 0;
|
||||
} else {
|
||||
count_boost_fail++;
|
||||
}
|
||||
}
|
||||
else if (!good_voltage) {
|
||||
pwm_on();
|
||||
}
|
||||
if (update_display) {
|
||||
show_current_radiation();
|
||||
}
|
||||
|
||||
// If fail to reach target voltage in reasonable time,
|
||||
// show weak battery icon and stop trying.
|
||||
if (count_boost_fail > 50000 && analog < adc_target16_acceptable) {
|
||||
// TODO weak battery icon
|
||||
sseg_number(&sseg, 9999, 5, 0);
|
||||
pwm_off();
|
||||
|
||||
for(;;) {}
|
||||
}
|
||||
|
||||
// TODO synchronization?
|
||||
if (tick_counter_changed) {
|
||||
tick_counter_changed = false;
|
||||
sseg_number(&sseg, tick_counter, 5, 0);
|
||||
}
|
||||
|
||||
// if (++cnt > 10000) {
|
||||
// sseg_number(&sseg, analog, 5, 0);
|
||||
// cnt = 0;
|
||||
// }
|
||||
if (is_weak_battery) {
|
||||
shutdown_due_to_weak_battery();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// try to preserve power; but the display will still drain the battery.
|
||||
// we should probably shut it off too.
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user