split to files

master
Ondřej Hruška 2 years ago
parent 8d1380bad6
commit 020c14a31f
  1. 15
      src/global_state.h
  2. 140
      src/high_voltage.c
  3. 14
      src/high_voltage.h
  4. 249
      src/main.c
  5. 52
      src/radiation.c
  6. 10
      src/radiation.h
  7. 1
      src/sevenseg.h
  8. 33
      src/time_base.c
  9. 12
      src/time_base.h
  10. 48
      src/user_interface.c
  11. 16
      src/user_interface.h

@ -0,0 +1,15 @@
/**
* Declarations of global variables
*/
#ifndef CPROJ_GLOBAL_VARIABLES_H
#define CPROJ_GLOBAL_VARIABLES_H
#include <stdbool.h>
#include <stdint.h>
extern volatile bool req_update_display;
extern volatile bool is_weak_battery;
extern volatile uint16_t timestamp_100ms;
#endif //CPROJ_GLOBAL_VARIABLES_H

@ -0,0 +1,140 @@
/**
* 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 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
/// 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();
}
}

@ -0,0 +1,14 @@
/**
* High voltage
*/
#ifndef CPROJ_HIGH_VOLTAGE_H
#define CPROJ_HIGH_VOLTAGE_H
/// Init HV source. Requires interrupts enabled!
void init_high_voltage();
/// Disable the HV source
void hv_disable();
#endif //CPROJ_HIGH_VOLTAGE_H

@ -1,229 +1,39 @@
#include "twi.h"
#include "ssd1306.h"
#include "sevenseg.h" #include "sevenseg.h"
#include "adc.h"
#include "iopins.h"
#include <avr/interrupt.h> #include <avr/interrupt.h>
#include <util/atomic.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(); /* globals */
static void show_empty_battery_icon(); volatile bool req_update_display = false;
static void shutdown_due_to_weak_battery(); volatile bool is_weak_battery = false;
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 = { static void shutdown_due_to_weak_battery();
.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;
}
// 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) void __attribute__((noreturn)) main()
{ {
// Service the boost converter init_user_interface();
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_timebase();
static void init_pcisr() { init_radiation();
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();
// --- let's go --- // --- let's go ---
sei(); sei();
vsen_adc_init(); // this needs interrupts enabled
init_high_voltage();
for (;;) { for (;;) {
_delay_ms(100); _delay_ms(100);
// check if redraw is needed // check if redraw is needed
bool update_display = false; bool update_display = false;
ATOMIC_BLOCK(ATOMIC_FORCEON) { ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
update_display = req_update_display; update_display = req_update_display;
req_update_display = false; 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() void __attribute__((noreturn)) shutdown_due_to_weak_battery()
{ {
pwm_off();
show_empty_battery_icon();
cli(); cli();
hv_disable();
show_empty_battery_icon();
// try to preserve power; but the display will still drain the battery. // try to preserve power; but the display will still drain the battery.
// we should probably shut it off too. // we should probably shut it off too.
for(;;) { for (;;) {
sleep_enable(); sleep_enable();
sleep_cpu(); 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);
}

@ -0,0 +1,52 @@
/**
* Radiation counting
*/
#include "radiation.h"
#include "time_base.h"
#include "global_state.h"
#include "iopins.h"
#include <avr/interrupt.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#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;
// geiger tube pin change interrupt
ISR(INT0_vect)
{
if (measurements[meas_bin] < MEAS_MAX_COUNT) {
measurements[meas_bin]++;
}
req_update_display = true;
}
void second_callback_irq()
{
meas_bin++;
if (meas_bin >= MEAS_BIN_COUNT) {
meas_bin = 0;
}
measurements[meas_bin] = 0;
req_update_display = true;
}
void init_radiation() {
// clear the measurement buffer
memset((void*) measurements, 0, sizeof(measurements));
as_input(D2);
// using INT0 - arduino pin D2
EICRA = _BV(ISC01) | _BV(ISC00); // rising edge
EIMSK = _BV(INT0);
}

@ -0,0 +1,10 @@
/**
* Radiation counting
*/
#ifndef CPROJ_RADIATION_H
#define CPROJ_RADIATION_H
void init_radiation();
#endif //CPROJ_RADIATION_H

@ -6,6 +6,7 @@
#define CPROJ_SEVENSEG_H #define CPROJ_SEVENSEG_H
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h>
struct SevenSeg { struct SevenSeg {
uint8_t x0; uint8_t x0;

@ -0,0 +1,33 @@
/**
* Time base
*/
#include "time_base.h"
#include <stdint.h>
#include <avr/interrupt.h>
/// timestamp counted in units of 100ms
volatile uint16_t timestamp_100ms = 0;
/// sub-second counter, counts 0-9 then overflows
static volatile uint16_t subsec_counter = 0;
// 100ms counter
ISR(TIMER1_COMPA_vect)
{
timestamp_100ms++;
subsec_counter++;
if (subsec_counter == 10) {
subsec_counter = 0;
second_callback_irq();
}
}
void init_timebase()
{
// CTC & presc=256
TCCR1B = (1 << WGM12) | (1 << CS12);
TIMSK1 = (1 << OCIE1A); // Enable CTC interrupt
OCR1A = 6250; // 100ms
}

@ -0,0 +1,12 @@
/**
* TODO file description
*/
#ifndef CPROJ_TIME_BASE_H
#define CPROJ_TIME_BASE_H
void second_callback_irq();
void init_timebase();
#endif //CPROJ_TIME_BASE_H

@ -0,0 +1,48 @@
/**
* Dispay
*/
#include "user_interface.h"
#include "twi.h"
#include "ssd1306.h"
#include "sevenseg.h"
#include "global_state.h"
static struct SevenSeg sseg = {
.x0 = 0,
.y0 = 0,
.charwidth = 17,
.thick = 3,
//.charwidth = 16,
//.thick = 1,
.spacing = 4,
};
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 show_current_radiation()
{
// TODO
sseg_number(&sseg, timestamp_100ms, 5, 0);
}
void show_empty_battery_icon()
{
// TODO
sseg_number(&sseg, 9999, 5, 0);
}
void show_loading_icon()
{
// TODO
sseg_number(&sseg, 0, 5, 0);
}

@ -0,0 +1,16 @@
/**
* UI
*/
#ifndef CPROJ_USER_INTERFACE_H
#define CPROJ_USER_INTERFACE_H
void init_user_interface();
void show_current_radiation();
void show_empty_battery_icon();
void show_loading_icon();
#endif //CPROJ_USER_INTERFACE_H
Loading…
Cancel
Save