split to files
This commit is contained in:
@@ -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
|
||||
+20
-229
@@ -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"
|
||||
|
||||
/* globals */
|
||||
volatile bool req_update_display = false;
|
||||
volatile bool is_weak_battery = false;
|
||||
|
||||
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)
|
||||
void __attribute__((noreturn)) main()
|
||||
{
|
||||
if (measurements[meas_bin] < MEAS_MAX_COUNT) {
|
||||
measurements[meas_bin]++;
|
||||
}
|
||||
req_update_display = true;
|
||||
}
|
||||
init_user_interface();
|
||||
|
||||
// 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
|
||||
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();
|
||||
cli();
|
||||
hv_disable();
|
||||
|
||||
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(;;) {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct SevenSeg {
|
||||
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
|
||||
Reference in New Issue
Block a user