/ teensy / Tone.cpp
Tone.cpp
  1  /* Tone generation for the Teensy and Teensy++
  2   * http://www.pjrc.com/teensy/
  3   * Copyright (c) 2010 PJRC.COM, LLC
  4   * 
  5   * Permission is hereby granted, free of charge, to any person obtaining a copy
  6   * of this software and associated documentation files (the "Software"), to deal
  7   * in the Software without restriction, including without limitation the rights
  8   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9   * copies of the Software, and to permit persons to whom the Software is
 10   * furnished to do so, subject to the following conditions:
 11   * 
 12   * The above copyright notice and this permission notice shall be included in
 13   * all copies or substantial portions of the Software.
 14   * 
 15   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 21   * THE SOFTWARE.
 22   */
 23  
 24  #include <avr/interrupt.h>
 25  #include "wiring.h"
 26  #include "core_pins.h"
 27  #include "pins_arduino.h"
 28  
 29  static uint8_t timer_acquired = 0;
 30  
 31  static uint8_t *tone1_reg = (uint8_t *)0;
 32  static uint8_t  tone1_mask = 0;
 33  static uint16_t tone1_inc = 0;
 34  static uint32_t tone1_count = 0;
 35  
 36  static uint8_t *tone2_reg = (uint8_t *)0;
 37  static uint8_t  tone2_mask = 0;
 38  static uint16_t tone2_inc = 0;
 39  static uint32_t tone2_count = 0;
 40  
 41  static uint8_t *tone3_reg = (uint8_t *)0;
 42  static uint8_t  tone3_mask = 0;
 43  static uint16_t tone3_inc = 0;
 44  static uint32_t tone3_count = 0;
 45  
 46  #define MAX_FREQ (F_CPU / 16 / 25)
 47  #define MIN_FREQ (F_CPU / 16 / 65535 + 1)
 48  
 49  
 50  
 51  #define PIN_REG_AND_MASK_LOOKUP(pin, reg, mask) \
 52          asm volatile(                           \
 53                  "lsl %2"                "\n\t"  \
 54                  "add %A3, %2"           "\n\t"  \
 55                  "adc %B3, __zero_reg__" "\n\n"  \
 56                  "lpm %1, Z+"            "\n\t"  \
 57                  "lpm %A0, Z"            "\n\t"  \
 58                  "ldi %B0, 0"            "\n"    \
 59                  : "=z" (reg), "=r" (mask), "+r" (pin)   \
 60                  : "z" (digital_pin_table_PGM), "2" (pin))
 61  
 62  #if defined(__AVR_ATmega32U4__)
 63  //#define TONE_USE_TIMER1
 64  #define TONE_USE_TIMER3
 65  #elif defined(__AVR_AT90USB1286__)
 66  //#define TONE_USE_TIMER1
 67  #define TONE_USE_TIMER3
 68  #elif defined(__AVR_AT90USB162__)
 69  #define TONE_USE_TIMER1
 70  #elif defined(__AVR_AT90USB646__)
 71  //#define TONE_USE_TIMER1
 72  #define TONE_USE_TIMER3
 73  #endif
 74  
 75  #ifdef TONE_USE_TIMER3
 76  #define TIMSKx	TIMSK3
 77  #define OCIExA	OCIE3A
 78  #define OCIExB	OCIE3B
 79  #define OCIExC	OCIE3C
 80  #define TCCRxA	TCCR3A
 81  #define WGMx0	WGM30
 82  #define TCCRxB	TCCR3B
 83  #define	CSx1	CS31
 84  #define TCNTx	TCNT3
 85  #define OCRxA	OCR3A
 86  #define OCRxB	OCR3B
 87  #define OCRxC	OCR3C
 88  #define TIFRx	TIFR3
 89  #define OCFxA	OCF3A
 90  #define OCFxB	OCF3B
 91  #define OCFxC	OCF3C
 92  #define VECTxA	TIMER3_COMPA_vect
 93  #define VECTxB	TIMER3_COMPB_vect
 94  #define VECTxC	TIMER3_COMPC_vect
 95  #endif
 96  #ifdef TONE_USE_TIMER1
 97  #define TIMSKx	TIMSK1
 98  #define OCIExA	OCIE1A
 99  #define OCIExB	OCIE1B
100  #define OCIExC	OCIE1C
101  #define TCCRxA	TCCR1A
102  #define WGMx0	WGM10
103  #define TCCRxB	TCCR1B
104  #define	CSx1	CS11
105  #define TCNTx	TCNT1
106  #define OCRxA	OCR1A
107  #define OCRxB	OCR1B
108  #define OCRxC	OCR1C
109  #define TIFRx	TIFR1
110  #define OCFxA	OCF1A
111  #define OCFxB	OCF1B
112  #define OCFxC	OCF1C
113  #define VECTxA	TIMER1_COMPA_vect
114  #define VECTxB	TIMER1_COMPB_vect
115  #define VECTxC	TIMER1_COMPC_vect
116  #endif
117  
118  
119  void tone(uint8_t pin, uint16_t frequency, uint32_t duration)
120  {
121  	uint8_t *reg;
122  	uint8_t mask;
123  	uint16_t inc;
124  	uint32_t count;
125  
126  	if (pin >= CORE_NUM_TOTAL_PINS) return;
127  	if (frequency < MIN_FREQ) {
128  		frequency = MIN_FREQ;
129  	} else if (frequency > MAX_FREQ) {
130  		frequency = MAX_FREQ;
131  	}
132  	inc = (F_CPU / 16 + frequency / 2) / frequency;
133  	if (duration) {
134  		count = duration * frequency / 500;
135  	} else {
136  		count = 0;
137  	}
138  	if (!timer_acquired) {
139  		TIMSKx = 0;			// disable all interrupts
140  		TCCRxA = 0;
141  		TCCRxB = (1<<CSx1);		// normal mode, div8 prescale
142  		timer_acquired = 1;
143  	}
144  	PIN_REG_AND_MASK_LOOKUP(pin, reg, mask);
145  
146  	if (!tone1_mask || (tone1_mask == mask && tone1_reg == reg)) {
147  		TIMSKx &= ~(1<<OCIExA);	// disable compare interrupt
148  		tone1_reg = reg;
149  		tone1_mask = mask;
150  		tone1_count = count;
151  		tone1_inc = inc;
152  		cli();
153  		*(reg + 2) &= ~mask;	// clear pin
154  		*(reg + 1) |= mask;	// output mode
155  		OCRxA = TCNTx + inc;
156  		TIFRx |= (1<<OCFxA);	// clear any pending compare match
157  		sei();
158  		TIMSKx |= (1<<OCIExA);	// enable compare interrupt
159  		return;
160  	}
161  	if (!tone2_mask || (tone2_mask == mask && tone2_reg == reg)) {
162  		TIMSKx &= ~(1<<OCIExB);	// disable compare interrupt
163  		tone2_reg = reg;
164  		tone2_mask = mask;
165  		tone2_count = count;
166  		tone2_inc = inc;
167  		cli();
168  		*(reg + 2) &= ~mask;	// clear pin
169  		*(reg + 1) |= mask;	// output mode
170  		OCRxB = TCNTx + inc;
171  		TIFRx |= (1<<OCFxB);	// clear any pending compare match
172  		sei();
173  		TIMSKx |= (1<<OCIExB);	// enable compare interrupt
174  		return;
175  	}
176  	if (!tone3_mask || (tone3_mask == mask && tone3_reg == reg)) {
177  		TIMSKx &= ~(1<<OCIExC);	// disable compare interrupt
178  		tone3_reg = reg;
179  		tone3_mask = mask;
180  		tone3_count = count;
181  		tone3_inc = inc;
182  		cli();
183  		*(reg + 2) &= ~mask;	// clear pin
184  		*(reg + 1) |= mask;	// output mode
185  		OCRxC = TCNTx + inc;
186  		TIFRx |= (1<<OCFxC);	// clear any pending compare match
187  		sei();
188  		TIMSKx |= (1<<OCIExC);	// enable compare interrupt
189  		return;
190  	}
191  }
192  
193  void noTone(uint8_t pin)
194  {
195  	uint8_t *reg;
196  	uint8_t mask;
197  
198  	if (pin >= CORE_NUM_TOTAL_PINS) return;
199  	PIN_REG_AND_MASK_LOOKUP(pin, reg, mask);
200  
201  	if (tone1_mask == mask && tone1_reg == reg) {
202  		TIMSKx &= ~(1<<OCIExA);
203  		tone1_mask = 0;
204  	} else if (tone2_mask == mask && tone2_reg == reg) {
205  		TIMSKx &= ~(1<<OCIExB);
206  		tone2_mask = 0;
207  	} else if (tone3_mask == mask && tone3_reg == reg) {
208  		TIMSKx &= ~(1<<OCIExC);
209  		tone3_mask = 0;
210  	}
211  	if (!tone1_mask && !tone2_mask && !tone3_mask) {
212  		TCCRxA = (1<<WGMx0);	// restore timer
213  		timer_acquired = 0;
214  	}
215  }
216  
217  
218  ISR(VECTxA)
219  {
220  	OCRxA += tone1_inc;
221  	*(tone1_reg) = tone1_mask;
222  	if (tone1_count > 0) {
223  		if ((--tone1_count) == 0) {
224  			*(tone1_reg + 2) &= ~tone1_mask;
225  			TIMSKx &= ~(1<<OCIExA);
226  			tone1_mask = 0;
227  			if (!tone2_mask && !tone3_mask) {
228  				TCCRxA = (1<<WGMx0);
229  				timer_acquired = 0;
230  			}
231  		}
232  	}
233  }
234  
235  ISR(VECTxB)
236  {
237  	OCRxB += tone2_inc;
238  	*(tone2_reg) = tone2_mask;
239  	if (tone2_count > 0) {
240  		if ((--tone2_count) == 0) {
241  			*(tone2_reg + 2) &= ~tone2_mask;
242  			TIMSKx &= ~(1<<OCIExB);
243  			tone2_mask = 0;
244  			if (!tone1_mask && !tone3_mask) {
245  				TCCRxA = (1<<WGMx0);
246  				timer_acquired = 0;
247  			}
248  		}
249  	}
250  }
251  
252  ISR(VECTxC)
253  {
254  	OCRxC += tone3_inc;
255  	*(tone3_reg) = tone3_mask;
256  	if (tone3_count > 0) {
257  		if ((--tone3_count) == 0) {
258  			*(tone3_reg + 2) &= ~tone3_mask;
259  			TIMSKx &= ~(1<<OCIExC);
260  			tone3_mask = 0;
261  			if (!tone1_mask && !tone2_mask) {
262  				TCCRxA = (1<<WGMx0);
263  				timer_acquired = 0;
264  			}
265  		}
266  	}
267  }
268  
269