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=76bed47f45f1f70895f4abc25bf1f9588ac8b988;hb=3aeea82781e9fc7532861ec6fc5e509efdc799ba;hpb=d3b9a34d6b153bd04e7d233c2e2bbcecc70a6fa5 diff --git a/firmware/main.c b/firmware/main.c index 76bed47..65801e8 100644 --- a/firmware/main.c +++ b/firmware/main.c @@ -21,21 +21,15 @@ * 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. - * 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. + * 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 @@ -106,9 +112,7 @@ static unsigned char power_levels[] = { #define N_POWER_LEVELS (sizeof(power_levels) / sizeof(power_levels[0])) static unsigned char power_level = 0; // selected power level -static unsigned char power_level_changed; // for visual feedback -#define LED_PWRCHANGE_COUNT 3 #define LED_BATTEMPTY_COUNT 60 /* timing by WDT */ @@ -124,9 +128,8 @@ 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 @@ -134,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) @@ -171,6 +176,7 @@ ISR(ADC_vect) batt_on = adcw << ADC_RUNAVG_SHIFT; } } + ADCSRA &= ~_BV(ADIE); } /* ===================== Timer/Counter1 for PWM ===================== */ @@ -179,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]; @@ -192,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) @@ -256,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) ); } @@ -368,13 +382,21 @@ static unsigned char battery_level() static void status_led_next_pattern() { static unsigned char battery_exhausted; + static unsigned char display_power_level; - if (power_level_changed) { - power_level_changed--; + 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) { + if (b_level > 1) { battery_exhausted = 0; } else if (battery_exhausted) { if (!--battery_exhausted) @@ -383,12 +405,13 @@ static void status_led_next_pattern() battery_exhausted = LED_BATTEMPTY_COUNT; } - n_blinks = b_level + 1; + n_blinks = b_level ? b_level : 1; + blink_on_time = b_level ? 4 : 2; + blink_off_time = 0; } - blink_on_time = 2; - blink_off_time = 1; - blink_counter = 10; + blink_counter = 12; + display_power_level = !display_power_level; } static void timer_blink() @@ -411,23 +434,13 @@ static void timer_blink() static void button_pressed(unsigned char button, unsigned char long_press) { // ignore simlultaneous button 1 and 2 press - // Note: we set power_level_changed after each button press, - // even when the power is at maximum, to provide visual feedback - // with status LED. if (long_press) { - if (button == 1) { - power_down(); - return; - } 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(); - return; } } else if (button == 2) { if (power_level < N_POWER_LEVELS-1) { @@ -435,7 +448,6 @@ static void button_pressed(unsigned char button, unsigned char long_press) } } } - power_level_changed = LED_PWRCHANGE_COUNT; status_led_next_pattern(); } @@ -476,12 +488,16 @@ 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] @@ -491,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); } @@ -543,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(); } }