/*
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
|          Program Name: DC-DC Converter - Generic version                                                              |
|                Author: Michael Krämer                                                                                 |
|                 Email: Michael@dk8pp.de                                                                               |
|                  Date: 07-Jun-2012                                                                                    |
|                  Tool: AVR Studio 6.0                                                                                 |
|                Device: ATtiny44A                                                                                      |
|               Version: 0.1                                                                                            |
|        Program status: only for testing                                                                               |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
|  Purpose: initial feasibility study                                                                                   |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       Includes                                                                                                        |
+-----------------------------------------------------------------------------------------------------------------------+
*/
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       Defines                                                                                                         |
+-----------------------------------------------------------------------------------------------------------------------+
*/
#define eeprom_write_byte_cond(addr,data) if (eeprom_read_byte(addr) != data) eeprom_write_byte(addr, data)
#define eeprom_write_word_cond(addr,data) if (eeprom_read_word(addr) != data) eeprom_write_word(addr, data)

#define READ_SW1  (PINB & (1<<PINB0))   /* read pushbutton SW1 (upper right one on PCB)*/
#define READ_SW2  (PINB & (1<<PINB1))   /* read pushbutton SW2 (lower right one on PCB) */
#define READ_PLUG (PINA & (1<<PINA6))   /* read status of power connector (plugged/unplugged) */

#define LED1_ON   do {(PORTA |= (1<<PA3)) ;} while(0)   /* switch LED1 on  (upper left on PCB, red) */
#define LED1_OFF  do {(PORTA &= ~(1<<PA3)) ;} while(0)  /* switch LED1 off (upper left on PCB, red) */
#define LED2_ON   do {(PORTA |= (1<<PA2)) ;} while(0)   /* switch LED2 on  (lower left on PCB, green) */
#define LED2_OFF  do {(PORTA &= ~(1<<PA2)) ;} while(0)  /* switch LED2 off (lower left on PCB, green) */

#define DEBOUNCE_MASK 0x0f /* for debouncing: mask the bits, which must be the same, before level change is detected */

/* UBATT_MIN ist the minimum battery voltage, below which the system shuts down
   1100mV at the AD input generates a ADC result of 1023.
   We have a 39k-10k resistor network as voltage divider, which increases that value by a factor of 4.9.
   Therefore 1023 means 5.39V battery voltage. */
#define UBATT_MIN 569 /* 3V */
#define MAX_DC 0xa0 /* maximum duty cycle; wild guess for now */

#define BUZZER_FREQ_HIGHEST 250 /* ~4 kHz @ 1 MHz input clock */
#define BUZZER_VOL_HIGHEST  (BUZZER_FREQ_HIGHEST/20) /* duty cycle 1:20 */
#define BUZZER_TIME_HIGHEST 400 /* beep time in number of pulses */

#define BUZZER_FREQ_HIGHER 333 /* ~3 kHz @ 1 MHz input clock */
#define BUZZER_VOL_HIGHER  (BUZZER_FREQ_HIGHER/20) /* duty cycle 1:20 */
#define BUZZER_TIME_HIGHER 300 /* beep time in number of pulses */

#define BUZZER_FREQ_LOWER 500 /* ~2 kHz @ 1 MHz input clock */
#define BUZZER_VOL_LOWER  (BUZZER_FREQ_LOWER/20) /* duty cycle 1:20 */
#define BUZZER_TIME_LOWER 200 /* beep time in number of pulses */

#define BUZZER_FREQ_OFF 1000 /* ~1 kHz @ 1 MHz input clock */
#define BUZZER_VOL_OFF  (BUZZER_FREQ_OFF/20) /* duty cycle 1:20 */
#define BUZZER_TIME_OFF 100 /* beep time in number of pulses */

#define BUZZER_FREQ_ALARM 250 /* ~4 kHz @ 1 MHz input clock */
#define BUZZER_VOL_ALARM  (BUZZER_FREQ_ALARM/5) /* duty cycle 1:20 */
#define BUZZER_TIME_ALARM 200 /* beep time in number of pulses */

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       structs and enums                                                                                               |
+-----------------------------------------------------------------------------------------------------------------------+
*/
typedef enum {ALIGN_NO=0, ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT} ALIGN ;
typedef enum {OP_NO=0, OP_SET, OP_CLR, OP_INV} OPERATION ;
typedef enum {PLUG_UNKNOWN=0, PLUG_IN, PLUG_OUT} PLUG_STATUS ;
typedef enum {PB_UNKNOWN=0, PB_PUSHED, PB_RELEASED} PB_STATUS ;

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       Function prototypes                                                                                             |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void init_CPU(void) ;
void active_wait(uint16_t time) ;
uint16_t get_adc(uint8_t channel) ;
void beep(uint16_t time, uint16_t freq, uint16_t volume) ;
void active_wait_timer(uint16_t time) ;

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       global variables                                                                                                |
+-----------------------------------------------------------------------------------------------------------------------+
*/
//uint16_t EEMEM perm_voltage[16] ;

static volatile uint8_t sw1_history = 0 ;
static volatile uint8_t sw2_history = 0 ;
static volatile uint8_t plug_history = 0 ;
static volatile PB_STATUS sw1_status = PB_UNKNOWN ;
static volatile PB_STATUS sw2_status = PB_UNKNOWN ;
static volatile PLUG_STATUS plug_status = PLUG_UNKNOWN ;
static volatile uint8_t led1_ontime = 0 ;
static volatile uint8_t led1_offtime = 0 ;
static volatile uint8_t led1_flash = 0 ;
static volatile uint8_t led2_ontime = 0 ;
static volatile uint8_t duty_cycle = 0x10 ;
static volatile bool duty_cycle_valid = false ;
static volatile bool power_transistor_on = false ;
static volatile bool buzzing = false ;          /* true when buzzing, false otherwise */
static volatile uint16_t buzzer_time = 0 ;      /* time for a beep in ms */
static volatile uint16_t buzzer_freq = 0 ;      /* frequency of a beep */
static volatile uint16_t buzzer_vol = 0 ;       /* volume of a beep */
static volatile uint16_t mytimer ;              /* general purpose timer. Decremented by 10ms int handler */

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       Main                                                                                                            |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| MAIN program.                                                                                                         |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
int main(void)
{ /* executed on coldstart */
uint16_t i ;
uint8_t target_index ;
bool incremented ;
bool decremented ;
bool power_low ;
int16_t delta ;
uint16_t ubatt ;
uint16_t ubatt_accu ;
uint8_t ubatt_cycle ;
/* We generate output powers of 0W, 1W, 2W, 3W or 4W. At a 20 ohm load, that is 0V, 4.47V, 6.32V, 7.75V and 8.94V
   1100mV at the AD input generates a ADC result of 1023.
   We have a 100k-10k resistor network as voltage divider, which increases that value by a factor of 11.
   Therefore 1023 means 12.1V output voltage. The following targets are ADC readings for the above target voltages. */
int16_t target_voltage[] = {0, 378, 535, 655, 756} ;

   init_CPU() ; /* initialize CPU */
   target_index = 0 ;
   duty_cycle = 0 ;
   ubatt = UBATT_MIN + 1 ; /* start with valid battery voltage */
   ubatt_cycle = 0 ;
   ubatt_accu = 0 ;
   power_transistor_on = false ;
   power_low = false ;

   for (;;)
   {
      if ((sw1_status == PB_PUSHED) && (plug_status == PLUG_IN))
      { /* decrement target voltage */
         if (!decremented)
         {
            if (target_index > 0) target_index-- ;
            decremented = true ;
            if (target_index != 0)
            {
               led1_ontime = target_index * 10 ;
               beep(BUZZER_TIME_LOWER, BUZZER_FREQ_LOWER, BUZZER_VOL_LOWER) ;
            }
            else
            {
               led2_ontime = 50 ;
               beep(BUZZER_TIME_OFF, BUZZER_FREQ_OFF, BUZZER_VOL_OFF) ;
            }
         }
      }
      else
      {
         decremented = false ;
      }

      if ((sw2_status == PB_PUSHED) && (plug_status == PLUG_IN))
      { /* increment target voltage */
         if (!incremented)
         {
            if (target_index < (sizeof(target_voltage)/sizeof(target_voltage[0])-1)) target_index++ ;
            incremented = true ;
            led1_ontime = target_index * 10 ;
            if (target_index < (sizeof(target_voltage)/sizeof(target_voltage[0])-1))
               beep(BUZZER_TIME_HIGHER, BUZZER_FREQ_HIGHER, BUZZER_VOL_HIGHER) ;
            else
               beep(BUZZER_TIME_HIGHEST, BUZZER_FREQ_HIGHEST, BUZZER_VOL_HIGHEST) ;
         }
      }
      else
      {
         incremented = false ;
      }

      if (ubatt_cycle < 32)
      { /* calculate average UBATT over 32 cycles */
         ubatt_accu += get_adc(1) ; /* accumulate measured battery voltage */
         ubatt_cycle++ ;
      }

      if (ubatt_cycle >= 32)
      {
         ubatt = ubatt_accu / ubatt_cycle ;
         ubatt_cycle = 0 ;
         ubatt_accu = 0 ;
      }

      if ((!power_low) && (target_voltage[target_index] > 0) && (ubatt > UBATT_MIN) && (plug_status == PLUG_IN))
      { /* switch power transistor on */
         if (!power_transistor_on)
         {
            for (i=0; i<65000; i++)
            {/* battery voltage may break down!!! We start with pulses */
               PORTA &= ~(1<<PA7) ;
               PORTA |= (1<<PA7) ;
               active_wait(1) ;
            }
            for (i=0; i<65000; i++)
            {
               PORTA &= ~(1<<PA7) ;
               PORTA |= (1<<PA7) ;
            }
            TCCR0A = 0x83 ;     /* start PWM: fast PWM mode, clear OC0A on Compare Match, set at bottom */
            power_transistor_on = true ;
         }
         PORTA &= ~(1<<PA7) ;
         delta = target_voltage[target_index] - get_adc(0) ;
         duty_cycle_valid = false ;
         if (delta > 0) duty_cycle++ ;
         if ((delta < 0) && (duty_cycle != 0)) duty_cycle-- ;
         if (duty_cycle > MAX_DC) duty_cycle = MAX_DC ;
         duty_cycle_valid = true ; /* note: OCR0A is updated in the interrupt handler! */
      }
      else
      { /* switch power transistor off */
         if (ubatt <= UBATT_MIN) power_low = true ;
         PORTA |= (1<<PA7) ;
         TCCR0A = 0x03 ;        /* stop PWM, switch PB2 to port mode */
         PORTB &= ~(1<<PB2) ;   /* make sure that PB2 is low */
         power_transistor_on = false ;
         target_index = 0 ;
      }

      if (power_low)
      {
//         led1_flash = 100 ; /* flash red LED */
//         while (led1_flash != 0) ;
         for (i=0; i<10; i++)
         {
            beep(BUZZER_TIME_ALARM, BUZZER_FREQ_ALARM, BUZZER_VOL_ALARM) ;
            active_wait_timer(100) ;
         }
         for (i=0; i<5; i++)
         {
            beep(BUZZER_TIME_ALARM, BUZZER_FREQ_ALARM, BUZZER_VOL_ALARM) ;
            active_wait_timer(60000) ; /* 1 min */
         }
         beep(BUZZER_TIME_OFF, BUZZER_FREQ_OFF, BUZZER_VOL_OFF) ;
         active_wait_timer(1000) ; /* wait until buzzer has finished, 1 sec */
         ADCSRA &= ~(1<<ADEN) ; /* disable ADC */
         PRR |= ((1<<PRTIM1) | (1<<PRTIM0) | (1<<PRUSI) | (1<<PRADC)) ; /* Switch off timer 1, 2 and ADC */
         for (;;)
         { /* loop, in case somebody wakes us up */
            MCUCR = ((1<<SM1) | (1<<SE)) ;        /* power down mode and sleep mode enable */
            __asm("sleep") ;
         }
      }
   }
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       init_CPU                                                                                                        |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Initialize device                                                                                                     |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void init_CPU(void)
{ /* we run from the internal 8 MHz calibrated oscillator */
/* Switch off the watchdog timer. Note that C-Code is too slow if unoptimised. WDCE persists for four clocks only. */
   __asm("cli") ; /* Turn off global interrupt */
   __asm("wdr") ; /* Reset Watchdog Timer */
   __asm("push r16") ;
   __asm("ldi r16,0x18") ;
   __asm("sts 0x60,r16") ; /* Write logical one to WDCE and WDE of WDTCSR */
   __asm("ldi r16,0x00") ;
   __asm("sts 0x60,r16") ; /* Turn off WDT */
   __asm("pop r16") ;

/* Port A */
   PORTA = ((1<<PA7) | (1<<PA6) | (1<<PA4)) ;   /* main switch transistor is off (high), enable pull-ups on unused input ports, LEDs off */
   DDRA  = (1<<DDA7) | (1<<DDA5) | (1<<DDA3) | (1<<DDA2) ;  /* PA7 is main switch (low = on), PA5 (=OC1B) is beeper, PA2 is red LED, PA3 is green LED */

/* Port B */
   DDRB  = 1<<PB2 ;                     /* PB2 is output (PWM), others are input (PB0 and PB1 are pushbuttons) */
   PORTB = 1<<PB0 | 1<<PB1 | 1<<PB3 ;   /* enable pull-ups for inputs, PB2 output is low for now (switching transistor is off) */

/* Timer 0 is used for the voltage conversion PWM */
/* we use 8-bit Timer 0 in fast PWM mode, 8 MHz internal oscillator => 31.25kHz PWM clock */
//   TCCR0A = 0x83 ;      /* fast PWM mode, clear OC0A on Compare Match, set at bottom */
   TCCR0A = 0x03 ;      /* fast PWM mode, leave OC0A in port mode (level is low, transistor is off) */
   TCCR0B = 0x01 ;      /* use IO-clock, select fast PWM mode */
   OCR0A = duty_cycle ; /* set duty cycle */
   TIFR0 = ((1<<OCF0B) | (1<<OCF0A) | (1<<TOV0)) ; /* clear any possibly pending interrupt */
   TIMSK0 |= (1<<TOIE0) ; /* enable timer0 overflow interrupt */

/* ADC */
   ADMUX = 0x81 ;  /* select internal 1.1V voltage reference, select ADC1 (VBATT) as input */
   ADCSRA = 0x87 ; /* enable ADC, don't start conversion, no auto trigger, no interrupt,
                      prescaler divides by 128 (ADC runs at 62.5kHz at 8 MHz oscillator */
   ADCSRB = 0x00 ; /* unipolar mode, analog comparator multiplexer disable, right adjust, free running */
   DIDR0 = 0x03 ;  /* disable digital input on PA0 and PA1 */

/* Timer 1 is used for 10ms interrupts and for the beeper */
/* Init 16-bit Timer 1 for 10ms interrupts */
   TCCR1A = 0x00 ;     /* Clear Timer on Compare: Clear when timer equals OCR1A; OC1A/B disconnected */
   TCCR1B = 0x0a ;     /* CTC mode, clk(I/O)/8 (=1MHz) */
   TCCR1C = 0x00 ;     /* unused */
   OCR1A = 10000 ;     /* 100 Hz, 1 interrupt per 10 ms */
   TIFR1 = ((1<<ICF1) | (1<<OCF1B) | (1<<OCF1A) | (1<<TOV1)) ; /* clear any possibly pending interrupt */
   TIMSK1 = (1<<OCIE1A) ; /* enable Output Compare 1A interrupt */

   __asm("sei") ; /*  Turn on global interrupt */
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       get_adc                                                                                                         |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
|   Get voltage from channel.                                                                                           |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
uint16_t get_adc(uint8_t channel)
{
uint16_t result ;
   channel &= 0x07 ;
   ADMUX = (ADMUX & ((1<<REFS1)|(1<<REFS0))) | channel ;  /* select ADC channel */
   ADCSRA |= (1<<ADIF) ; /* clear ADIF flag */
   while ((ADCSRA & (1<<ADIF)) != 0) ; /* wait until ADIF is clear */
   ADCSRA |= (1<<ADSC) ; /* start conversion */
   while ((ADCSRA & (1<<ADIF)) == 0) ; /* wait until conversion is completed, i.e. ADIF set */
   result = ADC ;
   return(result) ;
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       active_wait_timer                                                                                               |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
|   Wait for time milliseconds.                                                                                         |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void active_wait_timer(uint16_t time)
{
   if (mytimer == 0) mytimer = (time/10) + 1 ; /* 10ms resolution */
   while (mytimer != 0)
   { /* go idle */
      MCUCR = (1<<SE) ;         /* select idle mode and enable sleep mode */
      __asm("sleep") ;
      MCUCR = 0 ;               /* clear sleep mode enable */
   }
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       active_wait                                                                                                     |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
|   Wait for at least time microseconds.                                                                                |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void active_wait(uint16_t time)
{
   for ( ; time != 0; time--)
   {
      __asm("nop") ;
      __asm("nop") ;
      __asm("nop") ;
      __asm("nop") ;
      __asm("nop") ;
      __asm("nop") ;
      __asm("nop") ;
      __asm("nop") ;
      __asm("nop") ;
      __asm("nop") ;
      __asm("nop") ;
      __asm("nop") ;
      __asm("nop") ;
   }
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       beep                                                                                                            |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Generate acoustic beep.                                                                                               |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
void beep(uint16_t time, uint16_t freq, uint16_t volume)
{
   __asm("cli") ; /* Turn off global interrupt */
   buzzer_time = time ;
   buzzer_freq = freq ;
   buzzer_vol  = volume ;
   __asm("sei") ; /*  Turn on global interrupt */
/* NOTE: This should be a proper "restore interrupts" */
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       TIM0_OVF                                                                                                        |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Interrupt handler for timer0 overflow. This is a 32µs interrupt at 31.25kHz clock frequency!                          |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
ISR(TIM0_OVF_vect)
{
/* Reload OCR0A  */
   if (duty_cycle_valid)
   {
      if (power_transistor_on) OCR0A = duty_cycle ; else OCR0A = 0 ;
      duty_cycle_valid = false ;
   }
}

/*
+-----------------------------------------------------------------------------------------------------------------------+
|       TIM1_COMPA                                                                                                      |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Interrupt handler for timer1 compare match A. 10ms interrupt.                                                         |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
ISR(TIM1_COMPA_vect)
{
/* Read and debounce push button 1 */
   sw1_history = (sw1_history<<1) + ((READ_SW1==0)?1:0) ;
   if (sw1_status != PB_PUSHED)
   {
      if ((sw1_history&DEBOUNCE_MASK) == DEBOUNCE_MASK)
      { /* for debouncing */
         sw1_status = PB_PUSHED ;
      }
   }
   if (sw1_status != PB_RELEASED)
   {
      if ((sw1_history&DEBOUNCE_MASK) == 0x00)
      { /* for debouncing */
         sw1_status = PB_RELEASED ;
      }
   }

/* Read and debounce push button 2 */
   sw2_history = (sw2_history<<1) + ((READ_SW2==0)?1:0) ;
   if (sw2_status != PB_PUSHED)
   {
      if ((sw2_history&DEBOUNCE_MASK) == DEBOUNCE_MASK)
      { /* for debouncing */
         sw2_status = PB_PUSHED ;
      }
   }
   if (sw2_status != PB_RELEASED)
   {
      if ((sw2_history&DEBOUNCE_MASK) == 0x00)
      { /* for debouncing */
         sw2_status = PB_RELEASED ;
      }
   }

/* Read and debounce plug status */
   plug_history = (plug_history<<1) + ((READ_PLUG==0)?0:1) ;
   if (plug_status != PLUG_IN)
   {
      if ((plug_history&DEBOUNCE_MASK) == DEBOUNCE_MASK)
      { /* for debouncing */
         plug_status = PLUG_IN ;
      }
   }
   if (plug_status != PLUG_OUT)
   {
      if ((plug_history&DEBOUNCE_MASK) == 0x00)
      { /* for debouncing */
         plug_status = PLUG_OUT ;
      }
   }

   if (led1_flash != 0)
   {
      if ((led1_ontime == 0) && (led1_offtime == 0)) led1_ontime = 5 ;
      else
      {
         if (led1_ontime != 0) led1_ontime-- ;
         if (led1_ontime != 0) LED1_ON ;
         else
         {
            LED1_OFF ;
            if (led1_offtime == 0) led1_offtime = 5 ;
         }
         if (led1_offtime == 1) led1_flash-- ;
         if (led1_offtime != 0) led1_offtime-- ;
      }
   }
   else
   {
      if (led1_ontime != 0)
      {
         if (led1_ontime != 1) LED1_ON ; else LED1_OFF ;
         led1_ontime-- ;
      }
   }

   if (led2_ontime != 0)
   {
      if (led2_ontime != 1) LED2_ON ; else LED2_OFF ;
      led2_ontime-- ;
   }

   if (buzzer_time != 0)
   { /* reconfigure timer 1 to generate beeps via PA5 (=OC1B) */
      if (!buzzing)
      { /* fast PWM mode (mode 14), TOP=ICR1, Clear OC1B on OCR1B Compare Match, set at BOTTOM (non-inverting) */
         TCCR1A = 0x22 ;        /* Clear OC1B on Compare Match, WGM1[1:0] = 10 (fast PWM, TOP=ICR1) */
         TCCR1B = 0x1a ;        /* WGM1[3:2] = 11 ((fast PWM, TOP=ICR1)), clk(I/O)/8 (=1MHz) */
         TCCR1C = 0x00 ;        /* unused */
         ICR1 = buzzer_freq ;   /* frequency */
         OCR1B = buzzer_vol ;   /* volume */
         TIFR1 = ((1<<ICF1) | (1<<OCF1B) | (1<<OCF1A) | (1<<TOV1)) ; /* clear any possibly pending interrupt */
         TIMSK1 = (1<<OCIE1B) ; /* enable Output Compare 1B interrupt */
         buzzing = true ;
      }
   }

   if (mytimer != 0) mytimer-- ;
}


/*
+-----------------------------------------------------------------------------------------------------------------------+
|       TIM1_COMPB                                                                                                      |
+-----------------------------------------------------------------------------------------------------------------------+
|                                                                                                                       |
| Interrupt handler for timer1 compare match B. Beeper.                                                                 |
|                                                                                                                       |
+-----------------------------------------------------------------------------------------------------------------------+
*/
ISR(TIM1_COMPB_vect)
{
   if (buzzer_time > 0) buzzer_time-- ;
   else
   { /* Init 16-bit Timer 1 for 10ms interrupts */
      TCCR1A = 0x00 ;   /* Clear Timer on Compare: Clear when timer equals OCR1A; OC1A/B disconnected */
      TCCR1B = 0x0a ;   /* CTC mode, clk(I/O)/8 (=1MHz) */
      TCCR1C = 0x00 ;   /* unused */
      OCR1A = 10000 ;   /* 100 Hz, 1 interrupt per 10 ms */
      TIFR1 = ((1<<ICF1) | (1<<OCF1B) | (1<<OCF1A) | (1<<TOV1)) ; /* clear any possibly pending interrupt */
      TIMSK1 = (1<<OCIE1A) ; /* enable Output Compare 1A interrupt */
      buzzing = false ;
   }
}

