X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?p=heater.git;a=blobdiff_plain;f=firmware%2Fmain.c;h=65801e81b05fd74192238c0d9892c2d87c9c0b24;hp=d35fb21cf91a1d76643298fa0b12dd90c109b78a;hb=3aeea82781e9fc7532861ec6fc5e509efdc799ba;hpb=0ed054882561be8e1282c4e2a3d626294fc2420b diff --git a/firmware/main.c b/firmware/main.c index d35fb21..65801e8 100644 --- a/firmware/main.c +++ b/firmware/main.c @@ -21,22 +21,16 @@ * Buttons: * There are two buttons (+ and -). Any button can wake the system up from * the power-down state. - * TODO: When the system is woken up by the "-" button, - * it starts with the minimum output power, when it is woken up by the "+" - * button, it start with the maximum output power. * When running, the "-" button is used for decreasing the output power, * the "+" button is for increasing it. - * When on the lowest power state, the "-" button switches the system off. - * Long "-" button press switches the system off, long "+" button - * press sets the output power to maximum. + * Any long button press switches the system off. * * Status LED: * When powering up by a button press, the LED goes on to provide a visual * feedback, and is switched off after the button is released. - * TODO: After a button press, the # of blinks of the LED reflects the - * chosen output power level for some time. Afterwards, it displays - * the battery level. - * TODO: When the battery is completely exhausted, the output power is switched + * It displays the current power level and current battery voltage + * using # of blinks with different blinking lengths. + * When the battery is completely exhausted, the output power is switched * off, the LED keeps blinking for some time, and then the whole system is * switched off to avoid deep discharge of the battery. * @@ -59,6 +53,16 @@ #define WAKEUP_POLL 50 // msec #define WAKEUP_LIMIT 5 // times WAKEUP_POLL +// #define BUTTONS_REVERSE + +#ifdef BUTTONS_REVERSE +# define BUTTON1 PB0 +# define BUTTON2 PB1 +#else +# define BUTTON1 PB1 +# define BUTTON2 PB0 +#endif /* !BUTTONS_REVERSE */ + /* which state (output on or output off) are we measuring now */ static volatile unsigned char adc_type, adc_drop; #define ADC_RUNAVG_SHIFT 5 // running average shift on batt_on, batt_off @@ -75,15 +79,17 @@ static volatile uint16_t batt_on, batt_off; // measured voltage * (1024UL * (mV)) \ / (6UL * ADC_1100MV_VALUE)) >> 8)) static unsigned char batt_levels[] = { - MV_TO_ADC8(3350), - MV_TO_ADC8(3700), - MV_TO_ADC8(3900), + MV_TO_ADC8(3000), // below this, do not enable load, and switch off + MV_TO_ADC8(3150), // below this, switch off after some time + MV_TO_ADC8(3450), // battery low + MV_TO_ADC8(3800), // battery ok, above that almost full }; #define BATT_N_LEVELS (sizeof(batt_levels) / sizeof(batt_levels[0])) /* output power and PWM calculation */ #define PWM_TOP 255 #define PWM_MAX (PWM_TOP - 8) // to allow for ADC "batt_off" measurements +#define PWM_MIN 8 // to allow for ADC "batt_on" measurements /* * The values in power_levels[] array are voltages at which the load @@ -107,6 +113,8 @@ static unsigned char power_levels[] = { static unsigned char power_level = 0; // selected power level +#define LED_BATTEMPTY_COUNT 60 + /* timing by WDT */ static volatile unsigned char jiffies, next_clock_tick; @@ -114,14 +122,14 @@ static volatile unsigned char jiffies, next_clock_tick; #define BUTTON_SHORT_MIN 1 #define BUTTON_LONG_MIN 10 + /* ========= Analog to Digital Converter (battery voltage) ========== */ static void adc_init() { power_adc_enable(); - ADCSRA = _BV(ADEN) // enable - | _BV(ADPS1) | _BV(ADPS0) // clk/8 = 125 kHz - | _BV(ADIE); // enable IRQ + ADCSRA = _BV(ADEN) // enable + | _BV(ADPS1) | _BV(ADPS0); // clk/8 = 125 kHz ADMUX = _BV(REFS1) | _BV(MUX1) | _BV(MUX0); // 1.1V reference, PB3 pin, single-ended DIDR0 |= _BV(ADC3D); // PB3 pin as analog input @@ -129,15 +137,17 @@ static void adc_init() static void adc_susp() { - ADCSRA &= ~_BV(ADEN); // disable ADC + ADCSRA = 0; // disable ADC DIDR0 &= ~_BV(ADC3D); // disable analog input on PB3 power_adc_disable(); } -static void adc_start_measurement() +static void adc_start_measurement(unsigned char on) { - ADCSRA |= _BV(ADSC); + adc_drop = 1; + adc_type = on; + ADCSRA |= _BV(ADSC) | _BV(ADIE); } ISR(ADC_vect) @@ -166,6 +176,7 @@ ISR(ADC_vect) batt_on = adcw << ADC_RUNAVG_SHIFT; } } + ADCSRA &= ~_BV(ADIE); } /* ===================== Timer/Counter1 for PWM ===================== */ @@ -174,9 +185,18 @@ static void pwm_init() power_timer1_enable(); DDRB |= _BV(PB4); + PORTB &= ~_BV(PB4); // TCCR1 = _BV(CS10); // clk/1 = 1 MHz - TCCR1 = _BV(CS11) | _BV(CS13); // clk/512 = 2 kHz + // TCCR1 = _BV(CS11) | _BV(CS13); // clk/512 = 2 kHz + /* + * clk/64 = 16 kHz. We use PWM_MIN and PWM_MAX, so we have at least + * 8 full T/C1 cycles to do two ADC measurements. The ADC with 125 kHz + * clock can do about 7000-9000 measurement per second, so we should + * be safe both on low and high OCR1B values with this clock + */ + TCCR1 = _BV(CS12) | _BV(CS11) | _BV(CS10); + GTCCR = _BV(COM1B1) | _BV(PWM1B); OCR1C = PWM_TOP; // OCR1B = steps[0]; @@ -187,20 +207,19 @@ static void pwm_init() static void pwm_susp() { TCCR1 = 0; + TIMSK = 0; + GTCCR = 0; + PORTB &= ~_BV(PB4); } ISR(TIM1_OVF_vect) { - adc_drop = 2; - adc_type = 1; - adc_start_measurement(); + adc_start_measurement(1); } ISR(TIM1_COMPB_vect) { - adc_drop = 2; - adc_type = 0; - adc_start_measurement(); + adc_start_measurement(0); } static void pwm_set(unsigned char pwm) @@ -251,9 +270,9 @@ static void buttons_susp() static unsigned char buttons_pressed() { return ( - (PINB & _BV(PB0) ? 0 : 1) + (PINB & _BV(BUTTON1) ? 0 : 1) | - (PINB & _BV(PB1) ? 0 : 2) + (PINB & _BV(BUTTON2) ? 0 : 2) ); } @@ -341,22 +360,87 @@ static void power_down() hw_setup(); } +/* ============ Status LED blinking =================================== */ +static unsigned char blink_on_time, blink_off_time, n_blinks; +static unsigned char blink_counter; + +static unsigned char battery_level() +{ + unsigned char i, adc8; + + // NOTE: we use 8-bit value only, so we don't need lock to protect + // us against concurrently running ADC IRQ handler: + adc8 = batt_off >> 8; + + for (i = 0; i < BATT_N_LEVELS; i++) + if (batt_levels[i] > adc8) + break; + + return i; +} + +static void status_led_next_pattern() +{ + static unsigned char battery_exhausted; + static unsigned char display_power_level; + + if (display_power_level) { + n_blinks = power_level + 1; + if (batt_on >> 8 == batt_off >> 8) { // load unplugged + n_blinks = 2 * n_blinks; + blink_on_time = 0; + blink_off_time = 0; + } else { + blink_on_time = 2; + blink_off_time = 2; + } + } else { + unsigned char b_level = battery_level(); + if (b_level > 1) { + battery_exhausted = 0; + } else if (battery_exhausted) { + if (!--battery_exhausted) + power_down(); + } else { + battery_exhausted = LED_BATTEMPTY_COUNT; + } + + n_blinks = b_level ? b_level : 1; + blink_on_time = b_level ? 4 : 2; + blink_off_time = 0; + } + + blink_counter = 12; + display_power_level = !display_power_level; +} + +static void timer_blink() +{ + if (blink_counter) { + blink_counter--; + } else if (!status_led_is_on()) { + status_led_on(); + blink_counter = blink_on_time; + } else if (n_blinks) { + --n_blinks; + status_led_off(); + blink_counter = blink_off_time; + } else { + status_led_next_pattern(); + } +} + /* ======== Button press detection and handling ===================== */ static void button_pressed(unsigned char button, unsigned char long_press) { // ignore simlultaneous button 1 and 2 press if (long_press) { - if (button == 1) { - power_down(); - } else if (button == 2) { - power_level = N_POWER_LEVELS-1; - } + power_down(); + return; } else { // short press if (button == 1) { if (power_level > 0) { --power_level; - } else { - power_down(); } } else if (button == 2) { if (power_level < N_POWER_LEVELS-1) { @@ -364,6 +448,7 @@ static void button_pressed(unsigned char button, unsigned char long_press) } } } + status_led_next_pattern(); } static unsigned char button_state, button_state_time; @@ -397,63 +482,22 @@ static void timer_check_buttons() button_state_time = 0; } -/* ============ Status LED blinking =================================== */ -static unsigned char blink_on_time, blink_off_time, n_blinks; -static unsigned char blink_counter; - -static unsigned char battery_level() -{ - unsigned char i, adc8; - - // NOTE: we use 8-bit value only, so we don't need lock to protect - // us against concurrently running ADC IRQ handler: - adc8 = batt_off >> 8; - - for (i = 0; i < BATT_N_LEVELS; i++) - if (batt_levels[i] > adc8) - break; - - return i; -} - -static void status_led_next_pattern() -{ - - // for now, display the selected intensity - // n_blinks = power_level + 1; - n_blinks = battery_level() + 1; - blink_on_time = 0; - blink_off_time = 2; - blink_counter = 10; -} - -static void timer_blink() -{ - if (blink_counter) { - blink_counter--; - } else if (status_led_is_on()) { - status_led_off(); - blink_counter = blink_off_time; - } else if (n_blinks) { - --n_blinks; - status_led_on(); - blink_counter = blink_on_time; - } else { - status_led_next_pattern(); - } -} - +/* ===================== Output power control ======================== */ static void calculate_power_level() { uint32_t pwm; unsigned char batt_on8; - if (battery_level() == 0 || batt_on == 0) { + if (battery_level() == 0) { pwm_set(0); // TODO power_down() after some time return; } + if (!batt_on) { + batt_on = batt_off; + }; + batt_on8 = batt_on >> 8; pwm = (uint32_t)PWM_TOP * power_levels[power_level] @@ -463,9 +507,14 @@ static void calculate_power_level() if (pwm > PWM_MAX) pwm = PWM_MAX; + if (pwm < PWM_MIN) + pwm = PWM_MIN; + +#if 0 log_byte(0x10 + power_level); log_byte(batt_on8); log_byte(pwm & 0xFF); +#endif pwm_set(pwm); } @@ -515,6 +564,10 @@ int main() log_byte(batt_on >> 8); #endif } + if (jiffies == 0) { + log_byte(batt_on >> 8); + log_byte(batt_off >> 8); + } log_flush(); } }