#define F_CPU 9600000
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/eeprom.h>
typedef unsigned char bool;
#define true (0 == 0)
#define false (0 != 0)
#define MAX_U10BIT 0b0000001111111111
#define INTERVAL 3
#define CUR_MINIMAL_DIFF 50
#define RES_MINIMAL_DIFF 50
#define FREQ_DIV_OFFSET 2
#define FREQ_MAXIMAL_DIV 6
EEMEM unsigned int EEPROM_cur_edge;
EEMEM unsigned int EEPROM_res_edge;
EEMEM unsigned char EEPROM_frequency_dividor;
unsigned int cur_edge, res_edge;
unsigned char frequency_dividor;
unsigned char clk = 0;
bool tp_reset = false;
static void init_vars(void) {
if(MCUSR & (1 << EXTRF)) {
tp_reset = true;
MCUSR &= ~(1 << EXTRF);
}
}
static void init_pins(void) {
DDRB |= (1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB4);
}
static void init_interrupts(void) {
sleep_enable();
WDTCR = (1 << WDCE) | (1 << WDE);
WDTCR = (1 << WDTIE) | WDTO_1S;
sei();
}
void init_settings(void) {
cur_edge = eeprom_read_word(&EEPROM_cur_edge);
res_edge = eeprom_read_word(&EEPROM_res_edge);
frequency_dividor = eeprom_read_byte(&EEPROM_frequency_dividor);
}
static void toggle_load(bool state) {
if(state) {
PORTB |= (1 << PB1);
} else {
PORTB &= ~(1 << PB1);
}
}
static void blink_load(unsigned char count) {
for(unsigned char i = 0; i < count; ++i) {
_delay_ms(200);
toggle_load(true);
_delay_ms(200);
toggle_load(false);
}
}
static void stop(void) {
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
while(true) sleep_cpu();
}
static void toggle_amp(bool state) {
if(state) {
PORTB |= (1 << PB4);
_delay_ms(250);
} else {
PORTB &= ~(1 << PB4);
}
}
static void toggle_lpf(bool state) {
if(state) {
DDRB |= (1 << PB3);
} else {
DDRB &= ~(1 << PB3);
}
}
static void toggle_gen(bool state) {
if(state) {
TCCR0A |= (1 << COM0A0) | (1 << WGM01);
#ifndef PROTEUS
TCCR0B |= (1 << CS00);
#else
TCCR0B |= (1 << CS00) | (1 << CS02);
#endif
OCR0A = FREQ_DIV_OFFSET + frequency_dividor;
} else {
TCCR0A = 0;
}
}
static void toggle_adc(bool state) {
if(state) {
DDRB &= ~(1 << PB2);
ADMUX = 0b01 | (1 << REFS0);
ADCSRA = (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2) |
(1 << ADIE) |
(1 << ADEN);
} else {
ADCSRA = 0;
DDRB |= (1 << PB2);
_delay_ms(50);
}
}
static unsigned int do_adc(void) {
set_sleep_mode(SLEEP_MODE_ADC);
do {
sleep_cpu();
} while(ADCSRA & (1 << ADSC));
return ADC;
}
static unsigned int get_current(void) {
unsigned int cur;
toggle_lpf(true);
_delay_ms(150);
toggle_adc(true);
_delay_ms(50);
cur = do_adc();
toggle_adc(false);
toggle_lpf(false);
return cur;
}
static unsigned int get_resistance(void) {
unsigned int res;
toggle_gen(true);
_delay_ms(150);
toggle_adc(true);
_delay_ms(50);
toggle_gen(false);
res = do_adc();
toggle_adc(false);
return MAX_U10BIT - res;
}
static bool is_current(void) {
return (get_current() >= cur_edge);
}
static bool is_toggled_on(void) {
return (get_resistance() <= res_edge);
}
static void do_main(void) {
toggle_amp(true);
if(is_current()) {
toggle_load(false);
} else {
if(is_toggled_on()) {
toggle_load(true);
} else {
toggle_load(false);
}
}
toggle_amp(false);
}
static bool first_on(void) {
return (frequency_dividor == 0xff);
}
static void calibrate(void) {
unsigned int cur_off, cur_on, res_off, res_on, res_on_tmp, res_off_array[FREQ_MAXIMAL_DIV + 1], diff, max_diff, frequency_dividor_tmp;
blink_load(5);
_delay_ms(7500);
toggle_amp(true);
cur_off = get_current();
for(frequency_dividor = 0; frequency_dividor <= FREQ_MAXIMAL_DIV; ++frequency_dividor) {
res_off_array[frequency_dividor] = get_resistance();
}
blink_load(2);
_delay_ms(7500);
cur_on = get_current();
blink_load(3);
_delay_ms(7500);
res_off = MAX_U10BIT;
res_on = MAX_U10BIT;
frequency_dividor_tmp = 0;
max_diff = 0;
for(frequency_dividor = 0; frequency_dividor <= FREQ_MAXIMAL_DIV; ++frequency_dividor) {
res_on_tmp = get_resistance();
if(res_off_array[frequency_dividor] > res_on_tmp) {
diff = res_off_array[frequency_dividor] - res_on_tmp;
if(diff > max_diff) {
res_off = res_off_array[frequency_dividor];
res_on = res_on_tmp;
frequency_dividor_tmp = frequency_dividor;
max_diff = diff;
}
}
}
frequency_dividor = frequency_dividor_tmp;
toggle_amp(false);
if(cur_on > cur_off + CUR_MINIMAL_DIFF) {
cur_edge = cur_off + (cur_on - cur_off) / 2;
if(res_on + RES_MINIMAL_DIFF < res_off) {
res_edge = res_off - (res_off - res_on) / 2;
eeprom_write_word(&EEPROM_cur_edge, cur_edge);
eeprom_write_word(&EEPROM_res_edge, res_edge);
eeprom_write_byte(&EEPROM_frequency_dividor, frequency_dividor);
blink_load(1);
} else {
blink_load(2);
if(first_on()) stop();
}
} else {
blink_load(3);
if(first_on()) stop();
}
}
ISR(WDT_vect) {
WDTCR |= (1 << WDTIE);
}
EMPTY_INTERRUPT(ADC_vect);
int main(void)
{
init_vars();
init_pins();
init_interrupts();
init_settings();
if(tp_reset || first_on()) {
calibrate();
}
while(true) {
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_cpu();
if(++clk >= INTERVAL) {
do_main();
clk = 0;
}
}
}