* 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.
#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
/* 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
#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 */
{
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
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)
batt_on = adcw << ADC_RUNAVG_SHIFT;
}
}
+ ADCSRA &= ~_BV(ADIE);
}
/* ===================== Timer/Counter1 for PWM ===================== */
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];
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)
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)
);
}
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;
+ blink_on_time = 1;
+ blink_off_time = 2;
} else {
unsigned char b_level = battery_level();
if (b_level) {
}
n_blinks = b_level + 1;
+ blink_on_time = 3;
+ blink_off_time = 0;
}
- blink_on_time = 2;
- blink_off_time = 1;
blink_counter = 10;
+ display_power_level = !display_power_level;
}
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();
}
}
}
- power_level_changed = LED_PWRCHANGE_COUNT;
status_led_next_pattern();
}
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]
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);
}