diff --git a/src/graphic_empty_battery.c b/src/graphic_empty_battery.c new file mode 100644 index 0000000..df67c29 --- /dev/null +++ b/src/graphic_empty_battery.c @@ -0,0 +1,71 @@ +/** + * Empty battery + */ + +#include +#include "ssd1306.h" +#include "user_interface.h" + +void show_empty_battery() +{ + ssd1306_clearScreen(); + + // ********* + // **** * + // **** x * + // **** * + // ********* + +#define capwid 6 +#define hei 24 +#define caphei 12 +#define wid 50 +#define thic 2 +#define inpad 2 +#define charge 2 + +#define ofsx ((DISPLAY_W - capwid - wid)/2) +#define ofsy ((DISPLAY_H - hei)/2) + + const uint8_t rects[6][4] = { + // cap + { + 0, (hei - caphei) / 2, + capwid - 1, hei - (hei - caphei) / 2 - 1 + }, + // left line + { + capwid, 0, + capwid+thic - 1, hei - 1 + }, + // top line + { + capwid + thic, 0, + capwid + wid - thic - 1, thic - 1 + }, + // right line + { + capwid + wid - thic, 0, + capwid + wid - 1, hei - 1 + }, + // "remaining charge" line + { + capwid + wid - thic - inpad - charge, thic + inpad, + capwid + wid - thic - inpad - 1, hei - thic - inpad - 1 + }, + // bottom line + { + capwid + thic, hei - thic, + capwid + wid - thic - 1, hei - 1 + } + }; + + for (int i = 0; i < 6; i++) { + ssd1306_fillRect( + ofsx + rects[i][0], + ofsy + rects[i][1], + ofsx + rects[i][2], + ofsy + rects[i][3] + ); + } +} diff --git a/src/graphic_loading.c b/src/graphic_loading.c new file mode 100644 index 0000000..cf80812 --- /dev/null +++ b/src/graphic_loading.c @@ -0,0 +1,65 @@ +/** + * Loading + */ + +#include +#include +#include "ssd1306.h" +#include "user_interface.h" + + +void show_loading_screen(uint8_t progress_percent, bool clear) +{ + if (clear) { + ssd1306_clearScreen(); + } + + // bar in a box + +#define hei 20 +#define wid DISPLAY_W +#define thic 2 +#define inpad 3 + +#define ofsx ((DISPLAY_W - wid)/2) +#define ofsy ((DISPLAY_H - hei)/2) + + const uint8_t rects[4][4] = { + // top + { + 0, 0, + wid - 1, thic - 1 + }, + // left + { + 0, thic, + thic - 1, hei - thic - 1 + }, + // right + { + wid - thic, thic, + wid - 1, hei - thic - 1 + }, + // bot + { + 0, hei - thic, + wid - 1, hei - 1 + }, + }; + + for (int i = 0; i < 4; i++) { + ssd1306_fillRect( + ofsx + rects[i][0], + ofsy + rects[i][1], + ofsx + rects[i][2], + ofsy + rects[i][3] + ); + } +// + ssd1306_fillRect( + ofsx + thic + inpad, + ofsy + thic + inpad, + ofsx + (uint8_t)(((uint16_t)wid * (uint16_t)progress_percent)/(uint16_t)100) - thic - inpad - 1, + ofsy + hei - thic - inpad - 1 + ); +} diff --git a/src/high_voltage.c b/src/high_voltage.c index d512dc9..5da53ef 100644 --- a/src/high_voltage.c +++ b/src/high_voltage.c @@ -12,8 +12,8 @@ static volatile bool is_pwm_on = false; // tube starting voltage is 350V -#define TURN_ON_VOLTAGE 355.0 -#define TURN_OFF_VOLTAGE 380.0 +#define TURN_ON_VOLTAGE 360.0 +#define TURN_OFF_VOLTAGE 390.0 #define MINIMAL_VOLTAGE 350.0 // sensing via resistive divider diff --git a/src/main.c b/src/main.c index 7c627de..82501c1 100644 --- a/src/main.c +++ b/src/main.c @@ -19,6 +19,8 @@ void __attribute__((noreturn)) main() { init_user_interface(); + show_loading_screen(0, true); + init_timebase(); init_radiation(); @@ -54,10 +56,13 @@ void __attribute__((noreturn)) shutdown_due_to_weak_battery() cli(); hv_disable(); - show_empty_battery_icon(); + show_empty_battery(); // try to preserve power; but the display will still drain the battery. // we should probably shut it off too. + _delay_ms(2000); + turn_off_display(); + for (;;) { sleep_enable(); sleep_cpu(); diff --git a/src/radiation.c b/src/radiation.c index 356ac1d..bcc6318 100644 --- a/src/radiation.c +++ b/src/radiation.c @@ -8,23 +8,35 @@ #include "iopins.h" #include -#include +#include #include #include +#include #define MEAS_BIN_COUNT 120 -typedef uint8_t meas_bin_t; -#define MEAS_MAX_COUNT 255 +typedef uint8_t ticks_t; +typedef uint8_t binnum_t; +#define TICKS_MAX 255 + +#define NEEDED_TICKS_FOR_VALUE_DISPLAY 5 + +#define TICKS_NEEDED_FOR_AUTORANGE 15 + +uint16_t get_tick_count_in_bins(binnum_t bincount); /// measurements in the last X seconds -static volatile meas_bin_t measurements[MEAS_BIN_COUNT]; +static volatile ticks_t measurements[MEAS_BIN_COUNT]; /// Currently active measurement bin, increments once per second -static volatile uint8_t meas_bin = 0; +static volatile binnum_t meas_bin = 0; +static volatile binnum_t meas_oldest_bin = 0; +static volatile binnum_t num_live_bins = 1; + +static volatile binnum_t num_observed_bins = 60; // geiger tube pin change interrupt ISR(INT0_vect) { - if (measurements[meas_bin] < MEAS_MAX_COUNT) { + if (measurements[meas_bin] < TICKS_MAX) { measurements[meas_bin]++; } req_update_display = true; @@ -33,11 +45,41 @@ ISR(INT0_vect) void second_callback_irq() { meas_bin++; + if (num_live_bins < MEAS_BIN_COUNT) { + num_live_bins++; + } if (meas_bin >= MEAS_BIN_COUNT) { meas_bin = 0; } + + if (meas_oldest_bin == meas_bin) { + meas_oldest_bin++; + if (meas_oldest_bin >= MEAS_BIN_COUNT) { + meas_oldest_bin = 0; + } + } + measurements[meas_bin] = 0; req_update_display = true; + + /* autoranging */ + + uint16_t count; + bool found_range = 0; + for (int i = 5; i <= 60; i += 5) { + count = get_tick_count_in_bins(i); + if (count > TICKS_NEEDED_FOR_AUTORANGE) { + num_observed_bins = i * 2; + if (num_observed_bins > num_live_bins) { + num_observed_bins = num_live_bins; + } + found_range = true; + break; + } + } + if (!found_range) { + num_observed_bins = num_live_bins; + } } void init_radiation() { @@ -50,3 +92,89 @@ void init_radiation() { EICRA = _BV(ISC01) | _BV(ISC00); // rising edge EIMSK = _BV(INT0); } + +uint8_t rad_get_progress() { + ticks_t ticks = get_tick_count_in_bins(num_live_bins); + + if (ticks >= NEEDED_TICKS_FOR_VALUE_DISPLAY) { + return 100; + } + + return (((uint16_t)ticks * 100) / (uint16_t)NEEDED_TICKS_FOR_VALUE_DISPLAY); +} + +uint16_t get_tick_count_in_bins(binnum_t bincount) { + if (bincount == 0) { + return 0; + } + + uint16_t all_ticks = 0; + + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + + // going backwards + binnum_t bin = meas_bin; + for (;;) { + ticks_t in_bin = measurements[bin]; + if ((0xFFFF - all_ticks) < in_bin) { + all_ticks = 0xFFFF; + } else { + all_ticks += in_bin; + } + + if (bin == meas_oldest_bin) { + break; + } + + if (bincount > 0) { + bincount--; + if (bincount == 0) { + break; + } + } + + if (bin > 0) { + bin--; + } else { + bin = MEAS_BIN_COUNT - 1; + } + } + } + + return all_ticks; +} + +uint16_t rad_get_cpm() { + // bins take 1 second + + float count = get_tick_count_in_bins(num_observed_bins); + float cpm = (count / (float)num_observed_bins) * 60.0f; + return (uint16_t) roundf(cpm * 10.0f); +} + +uint16_t rad_get_usvh(uint8_t *out_decimals) { + float count = get_tick_count_in_bins(num_observed_bins); + float cpm = (count / (float)num_observed_bins) * 60.0f; + + // https://sites.google.com/site/diygeigercounter/technical/gm-tubes-supported + + float usvh = (cpm / 153.8f); + + if (usvh < 99.0f) { + *out_decimals = 3; + return (uint16_t) roundf(usvh * 1000.0f); + } + + if (usvh < 999.0f) { + *out_decimals = 2; + return (uint16_t) roundf(usvh * 100.0f); + } + + if (usvh < 9999.0f) { + *out_decimals = 1; + return (uint16_t) roundf(usvh * 10.0f); + } + + *out_decimals = 0; + return (uint16_t) roundf(usvh); +} diff --git a/src/radiation.h b/src/radiation.h index 3425993..9675211 100644 --- a/src/radiation.h +++ b/src/radiation.h @@ -5,6 +5,17 @@ #ifndef CPROJ_RADIATION_H #define CPROJ_RADIATION_H +#include +#include + void init_radiation(); +/// get progress 0-100, 100 = normal radiation resumed +uint8_t rad_get_progress(); + + +uint16_t rad_get_cpm(); +/// Get current radiation dose rate u uSv/h, x10 (one decimal place) +uint16_t rad_get_usvh(uint8_t *out_decimals); + #endif //CPROJ_RADIATION_H diff --git a/src/time_base.c b/src/time_base.c index 1c35387..7a4fb73 100644 --- a/src/time_base.c +++ b/src/time_base.c @@ -3,8 +3,10 @@ */ #include "time_base.h" +#include "global_state.h" #include +#include #include /// timestamp counted in units of 100ms @@ -17,6 +19,9 @@ ISR(TIMER1_COMPA_vect) { timestamp_100ms++; + + req_update_display = true; + subsec_counter++; if (subsec_counter == 10) { subsec_counter = 0; diff --git a/src/user_interface.c b/src/user_interface.c index 4a1f114..483a520 100644 --- a/src/user_interface.c +++ b/src/user_interface.c @@ -7,6 +7,9 @@ #include "ssd1306.h" #include "sevenseg.h" #include "global_state.h" +#include "radiation.h" + +static bool ended_loading = false; static struct SevenSeg sseg = { .x0 = 0, @@ -18,31 +21,56 @@ static struct SevenSeg sseg = { .spacing = 4, }; +void clear_screen() +{ + ssd1306_clearScreen(); +} + void init_user_interface() { TWI_Init(); ssd1306_128x32_i2c_init(); ssd1306_clearScreen(); - - // can't show anything useful at least until the first tick arrives, but more than 1 tick is needed - show_loading_icon(); } +void turn_off_display() { + ssd1306_displayOff(); +} +static volatile uint16_t last_value = 0; +static volatile uint16_t last_value_d = 0; void show_current_radiation() { - // TODO - sseg_number(&sseg, timestamp_100ms, 5, 0); -} + // this is called from main, so we can sleep -void show_empty_battery_icon() -{ - // TODO - sseg_number(&sseg, 9999, 5, 0); -} + if (!ended_loading) { + int progress = rad_get_progress(); + if (progress == 100) { + show_loading_screen(100, false); + ended_loading = true; -void show_loading_icon() -{ - // TODO - sseg_number(&sseg, 0, 5, 0); + // so user sees it + _delay_ms(250); + + clear_screen(); + // start showing values + } else { + if (progress < 10) progress = 10; + show_loading_screen(progress, false); + return; + } + } + +// uint16_t cpm = rad_get_cpm(); +// if (cpm != last_value) { +// last_value = cpm; +// sseg_number(&sseg, cpm, 5, 1); +// } + uint8_t decimals = 0; + uint16_t usvh = rad_get_usvh(&decimals); + if (usvh != last_value || decimals != last_value_d) { + last_value = usvh; + last_value_d = decimals; + sseg_number(&sseg, usvh, 5, decimals); + } } diff --git a/src/user_interface.h b/src/user_interface.h index 91d939a..e4101cf 100644 --- a/src/user_interface.h +++ b/src/user_interface.h @@ -5,12 +5,22 @@ #ifndef CPROJ_USER_INTERFACE_H #define CPROJ_USER_INTERFACE_H +#define DISPLAY_W 128 +#define DISPLAY_H 32 + +#include +#include + +void clear_screen(); + void init_user_interface(); void show_current_radiation(); -void show_empty_battery_icon(); +void show_empty_battery(); + +void show_loading_screen(uint8_t progress_percent, bool clear); -void show_loading_icon(); +void turn_off_display(); #endif //CPROJ_USER_INTERFACE_H