COSA
An Object-Oriented Platform for Arduino Programming
Tone.cpp
Go to the documentation of this file.
1 
21 #include "Cosa/Tone.hh"
22 #include "Cosa/Watchdog.hh"
23 
24 #if !defined(BOARD_ATTINY)
25 #if defined (__AVR_ATmega32U4__) \
26  || defined(__AVR_ATmega640__) \
27  || defined(__AVR_ATmega1280__) \
28  || defined(__AVR_ATmega1281__) \
29  || defined(__AVR_ATmega2560__) \
30  || defined(__AVR_ATmega2561__)
31 #define PWM1 DDB5
32 #define PWM2 DDB6
33 #define DDR DDRB
34 #define PORT PORTB
35 #elif defined(__AVR_ATmega1284P__) \
36  || defined(__AVR_ATmega644__) \
37  || defined(__AVR_ATmega644P__)
38 #define PWM1 DDD4
39 #define PWM2 DDD5
40 #define DDR DDRD
41 #define PORT PORTD
42 #elif defined(__AVR_ATmega256RFR2__)
43 #define PWM1 DDRB5
44 #define PWM2 DDRB6
45 #define DDR DDRB
46 #define PORT PORTB
47 #else
48 #define PWM1 DDB1
49 #define PWM2 DDB2
50 #define DDR DDRB
51 #define PORT PORTB
52 #endif
53 
54 uint32_t Tone::s_expires;
55 const uint8_t Tone::s_map[] __PROGMEM = {
56  200, 100, 67, 50, 40, 33, 29, 22, 11, 2
57 };
58 
59 void
61 {
62  // Initiate PWM pins as output
63  DDR |= (_BV(PWM1) | _BV(PWM2));
64 }
65 
66 void
67 Tone::play(uint16_t freq, uint8_t volume, uint16_t duration, bool background)
68 {
69  // Check if turn off tone
70  if (UNLIKELY((freq == 0) || (volume == 0))) {
71  silent();
72  return;
73  }
74 
75  // Check volume does not exceed limit
76  if (UNLIKELY(volume > VOLUME_MAX)) volume = VOLUME_MAX;
77 
78  // Calculate clock prescaling
80  uint8_t prescaler = _BV(CS10);
81  uint32_t top = (F_CPU / freq / 2) - 1;
82  if (top > 65535L) {
83  prescaler = _BV(CS12);
84  top = (top / 256) - 1;
85  }
86 
87  // Get duty from volume map
88  uint16_t duty = top / pgm_read_byte(&s_map[volume - 1]);
89 
90  // Check if interrupt handler should be enabled to turn off tone
91  if ((duration > 0) && background) {
92  s_expires = Watchdog::millis() + duration;
93  TIMSK1 |= _BV(OCIE1A);
94  }
95  ICR1 = top;
96  if (TCNT1 > top) TCNT1 = top;
97  TCCR1B = (_BV(WGM13) | prescaler);
98  OCR1A = duty;
99  OCR1B = duty;
100  TCCR1A = (_BV(COM1A1) | _BV(COM1B1) | _BV(COM1B0));
101 
102  // Check for asychronious mode
103  if ((duration == 0) || background) return;
104  delay(duration);
105  silent();
106 }
107 
108 void
110 {
111  // Turn off interrupt handler
112  TIMSK1 &= ~_BV(OCIE1A);
113  TCCR1B = _BV(CS11);
114  TCCR1A = _BV(WGM10);
115 
116  // Clear output pin
117  PORT &= ~(_BV(PIN1) | _BV(PIN2));
119 }
120 
122 {
123  // Check if the tone should be turned off
124  if (Watchdog::millis() < Tone::s_expires) return;
125  Tone::silent();
126 }
127 
128 #endif
129 
#define DDR
Definition: Tone.cpp:50
static uint32_t millis()
Definition: Watchdog.hh:51
const uint8_t Tone::s_map[] __PROGMEM
Definition: Tone.cpp:55
static void timer1_disable()
Definition: Power.hh:87
static void play(uint16_t freq, uint8_t volume=VOLUME_MAX/2, uint16_t duration=0, bool background=false)
Definition: Tone.cpp:67
static const uint8_t VOLUME_MAX
Definition: Tone.hh:72
static void silent()
Definition: Tone.cpp:109
#define TIMSK1
Definition: ATtinyX5.hh:229
void(* delay)(uint32_t ms)
static void begin()
Definition: Tone.cpp:60
#define PWM2
Definition: Tone.cpp:49
#define PWM1
Definition: Tone.cpp:48
friend void TIMER1_COMPA_vect(void)
#define PORT
Definition: Tone.cpp:51
ISR(TIMER1_COMPA_vect)
Definition: Tone.cpp:121
static void timer1_enable()
Definition: Power.hh:81
#define UNLIKELY(x)
Definition: Types.h:153