%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Author: Fabian Schuh
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Webseite: xeroc.org
% Lizenz: http://creativecommons.org/licenses/by-nc-sa/2.0/de/
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1 #include <avr/io.h>
  2 #include <avr/pgmspace.h>
  3 #include <avr/interrupt.h>
  4 #include <inttypes.h>
  5 #include "sound.h"
  6 
  7 #define FALSE   0
  8 #define TRUE    (!FALSE)
  9 
 10 #define F_CPU 1000000UL
 11 #define DURATION_SEED 32 
 12 
 13 #define sbiBF(port,bit)  (port |= (1<<bit))
 14 #define cbiBF(port,bit)  (port &= ~(1<<bit))
 15 
 16 const int FurElise[] PROGMEM={
 17 // 3,
 18  8,e2, 8,xd2, 8,e2, 8,xd2, 8,e2, 8,b1, 8,d2, 8,c2, 4,a1, 8,p,
 19  8,c1, 8,e1, 8,a1, 4,b1, 8,p, 8,e1, 8,xg1, 8,b1, 4,c2, 8,p, 8,e1,
 20  8,e2, 8,xd2, 8,e2, 8,xd2, 8,e2, 8,b1, 8,d2, 8,c2, 4,a1, 8,p, 8,c1,
 21  8,e1, 8,a1, 4,b1, 8,p, 8,e1, 8,c2, 8,b1, 4,a1,
 22  0, 1
 23 };
 24 
 25 const int *Song;
 26 static char Volume = 80;
 27 static char Duration = 0;
 28 static char Tone = 0;
 29 static char Tempo;
 30 void Play_Tune( void );
 31 
 32 /*************************************************************************
 33  *
 34  *************************************************************************/
 35 ISR( TIMER0_COMPA_vect ) {
 36  PORTA ^= 0xff;                         // Toggle PORTA, Anzeige des Rhythmus
 37  Play_Tune();
 38 }
 39 
 40 /*************************************************************************
 41  *
 42  *************************************************************************/
 43 void Play_Tune(void) {
 44  unsigned int temp_tone;
 45  char loop;
 46 
 47  if(!Tone) {
 48   PORTD ^= ( 1<<PIN4 );
 49   Duration = 0;
 50   Tone = 1;
 51  }
 52 
 53   if(Duration) {
 54    Duration--;
 55   } else if(pgm_read_word(Song + Tone)) {
 56    PORTD ^= ( 1<<PIN5 );
 57    Duration = ( DURATION_SEED / pgm_read_word(Song + Tone) );
 58    Tone++;
 59    temp_tone=pgm_read_word(Song + Tone);
 60    if( (temp_tone == p) | (temp_tone == P) )
 61     cbiBF(TCCR1B, CS10);
 62    else
 63     sbiBF(TCCR1B, CS10);
 64 
 65    cli();
 66    TCNT1H = 0;
 67    TCNT1L = 0;
 68    ICR1 = temp_tone;
 69    sei();
 70    Tone++;
 71   } else {
 72    Tone++;
 73    loop = (uint8_t)pgm_read_word(Song + Tone);
 74 
 75    if( loop ) {
 76     Tone = 1;
 77    } else {
 78     Tone = 0;
 79     cbiBF(TCCR1B, 0);
 80     TCCR1A = 0;
 81     TCCR1B = 0;
 82     sbiBF(PORTB, 5);
 83    }
 84   }
 85 }
 86 /*************************************************************************
 87  *
 88  * OC0A:PB2 -- OC1A:PB3 -- OC1B:PB4
 89  *************************************************************************/
 90 int main(void) {
 91  DDRD          = 0xff;                            // LEDs
 92  DDRB          = 0xff;                            // PWM Ausgang
 93 
 94  Song          = (int*) pgm_read_word(FurElise);  // Load Sond
 95  Duration      = 0;
 96  Tone          = 1;
 97 
 98  TCCR0A        = 0;
 99  TCCR0B        = 0;
100  TCCR1A        = 0;
101  TCCR1B        = 0;
102 
103  /*
104   * Timer 0 --> 8Bit --> OC0A --> PB2 --> Takt-Interrupt
105   * Timer/Counter0 is a general purpose 8-bit Timer/Counter module, with two independent
106   *  Output Compare Units, and with PWM support. It allows accurate program execution 
107   */
108  TIMSK         = (1<<OCIE0A);                     // Enable timer0 compare interrupt
109  OCR0A         = 0xB0;                            // Sets the compare value
110  TCCR0A        = (1<<WGM01);                      // Set Clear on Timer Compare (CTC) mode,
111  TCCR0B        = (4<<CS00);                       // CLK/256 prescaler
112 
113  /*
114   * Timer 1 --> 16Bit --> OC1A --> PWM --> Frequenzy
115   * Set up a 16-bits timer to run at Phase/Freq-correct PWM, top value = ICR1,
116   * set OC1A when upcounting, clear when downcounting.
117   */
118  TCCR1A        = ( 1<<COM1A1 ) |( 1<<COM1A0 );    // Set OC1A/OC1B on Compare Match, clear OC1A/OC1B at TOP
119  TCCR1B        = ( 1<<WGM13 ) |                   // PWM, Phase and Frequency Correct - Top: ICR1
120                  ( 1<<CS00 );
121 
122  ICR1          = C0;                              // sets frequency
123  OCR1AH        = 0;
124  OCR1AL        = 0x09;                            // Sets volume
125 
126  Duration = 0;
127  Tone = 1;
128 
129  sei();                                           // Enable global Interrupts
130  while( 1 );
131  return 0;
132 }