jetjaguar 0 Posted January 27, 2014 Share Posted January 27, 2014 Hello everyone, I'm trying to add some sound to my robot. The Energia example sketch "toneMelody" compiles without error unless you also include the library Servo.h. I have read that Arduinos have the same problem, it is because both libraries call upon the same timer. Is it possible to change the code for one of the libraries so that it uses a different timer, if one exists, so that the libraries do not conflict? I'm still a novice at all of this so any help would be much appreciated, thank you. I am using the m430g2553 chip on the Ti Launchpad MSP430 rev.1.5 board. Energia v0101e0011. Win7. simple code: #include <Servo.h> #include "pitches.h" // notes in the melody: int melody[] = { NOTE_C4, NOTE_G3,NOTE_G3, NOTE_A3, NOTE_G3,0, NOTE_B3, NOTE_C4}; // note durations: 4 = quarter note, 8 = eighth note, etc.: int noteDurations[] = { 4, 8, 8, 4,4,4,4,4 }; void setup() { // iterate over the notes of the melody: for (int thisNote = 0; thisNote < 8; thisNote++) { // to calculate the note duration, take one second // divided by the note type. //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc. int noteDuration = 1000/noteDurations[thisNote]; tone(8, melody[thisNote],noteDuration); // to distinguish the notes, set a minimum time between them. // the note's duration + 30% seems to work well: int pauseBetweenNotes = noteDuration * 1.30; delay(pauseBetweenNotes); // stop the tone playing: noTone(8); } } void loop() { // no need to repeat the melody. } Error message when code is compiled: core.a(Tone - Copy.cpp.o): In function `TIMER0_A0_ISR()': C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430/Tone - Copy.cpp:208: multiple definition of `__isr_9' Servo\Servo.cpp.o:C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\libraries\Servo/Servo.cpp:89: first defined here collect2: ld returned 1 exit status Quote Link to post Share on other sites
roadrunner84 466 Posted January 27, 2014 Share Posted January 27, 2014 Yes, if you're using the MSP430G2553 you have TimerA0 and TimerA1 to your disposal. You could modify either library to use TimerA1 instead of TimerA0. /* Tone.cpp A Tone Generator Library - Modified for Energia Implements up to 3 (software) PWM outputs using TIMERA0 compare registers and IRQ. Can use any digital output pin for pulse generation (c) 2012 - Peter Brier. Based on code Originally written by Brett Hagman This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Version Modified By Date Comments ------- ----------- -------- -------- 0001 B Hagman 09/08/02 Initial coding 0002 B Hagman 09/08/18 Multiple pins 0003 B Hagman 09/08/18 Moved initialization from constructor to begin() 0004 B Hagman 09/09/26 Fixed problems with ATmega8 0005 B Hagman 09/11/23 Scanned prescalars for best fit on 8 bit timers 09/11/25 Changed pin toggle method to XOR 09/11/25 Fixed timer0 from being excluded 0006 D Mellis 09/12/29 Replaced objects with functions 0007 M Sproul 10/08/29 Changed #ifdefs from cpu to register 0008 P Brier 12/05/28 Modified for TI MSP430 processor 0009 P Brier 12/05/29 Fixed problem with re-init of expired tone *************************************************/ #include "wiring_private.h" #include "pins_energia.h" #include "Energia.h" // local funcions static void initTimers(); static void setTimer(uint8_t n, unsigned int frequency, unsigned long duration); static void stopTimer(uint8_t n); // timer clock frequency set to clock/8, at F_CPU = 1MHZ this gives an output freq range of ~[1Hz ..65Khz] and at 16Mhz this is ~[16Hz .. 1MHz] #define F_TIMER (F_CPU/8L) #ifndef TONE_TIMER #define TONE_TIMER 0 #else #if TONE_TIMER == 1 #ifndef __MSP430_HAS_T1A3__ #error Tone: Timer A1 is not available on this microcontroller #endif #ifdef __MSP430_HAS_T1A3__ #define AVAILABLE_TONE_PINS 3 #define SETARRAY(a) a,a,a #else #define AVAILABLE_TONE_PINS 2 #define SETARRAY(a) a,a #endif #elif TONE_TIMER != 0 #error Tone: Do not know how to use a timer other than Timer A0 and Timer A1 #else #ifdef __MSP430_HAS_TA3__ #define AVAILABLE_TONE_PINS 3 #define SETARRAY(a) a,a,a #else #define AVAILABLE_TONE_PINS 2 #define SETARRAY(a) a,a #endif #endif #endif /* some pre-processor magic to allow TA_REG(CCR) to expand to TA0_CCR for TONE_TIMER == 0 */ #define _TONE_CONCAT(c, r) TA ## c ## r #define _TONE_RESOLVE(l, r) _TONE_CONCAT(l, r) #define TA_REG(name) _TONE_RESOLVE(TONE_TIMER, name) // tone_duration: // > 0 - duration specified // = 0 - stopped // < 0 - infinitely (until stop() method called, or new play() called) static uint8_t tone_state = 0; // 0==not initialized, 1==timer running static uint8_t tone_pins[AVAILABLE_TONE_PINS] = { SETARRAY(255) }; static uint8_t tone_bit[AVAILABLE_TONE_PINS] = { SETARRAY(255) }; volatile static uint8_t *tone_out[AVAILABLE_TONE_PINS] = { SETARRAY(0) }; static uint16_t tone_interval[AVAILABLE_TONE_PINS] = { SETARRAY(-1) }; static int16_t tone_periods[AVAILABLE_TONE_PINS] = { SETARRAY(0) }; /** *** tone() -- Output a tone (50% Dutycycle PWM signal) on a pin *** pin: This pin is selected as output *** frequency: [Hertz] ** duration: [milliseconds], if duration <=0, then we output tone continously, otherwise tone is stopped after this time (output = 0) **/ void tone(uint8_t _pin, unsigned int frequency, unsigned long duration) { uint8_t port = digitalPinToPort(_pin); if (port == NOT_A_PORT) return; // find if we are using it at the moment, if so: update it for (int i = 0; i < AVAILABLE_TONE_PINS; i++) { if (tone_pins[i] == _pin) { setTimer(i, frequency, duration); return; // we are done, timer reprogrammed } } // new tone pin, find empty timer and set it for (int i = 0; i < AVAILABLE_TONE_PINS; i++) { if (tone_pins[i] == 255) { tone_pins[i] = _pin; tone_bit[i] = digitalPinToBitMask(_pin); tone_out[i] = portOutputRegister(port); if ( tone_state == 0 ) initTimers(); pinMode(_pin, OUTPUT); setTimer(i, frequency, duration); return; // we are done, timer set } } // if we exit here, no unused timer was found, nothing is done } /** *** noTone() - Stop outputting the tone on a pin **/ void noTone(uint8_t _pin) { if ( _pin == 255 ) return; // Should not happen! for (int i = 0; i < AVAILABLE_TONE_PINS; i++) { if (tone_pins[i] == _pin) { tone_pins[i] = 255; stopTimer(i); } } } // Initialize the timers - Set mode and Enable IRQ static void inline initTimers() { // disable IRQs TA_REG(CCTL0) = 0; TA_REG(CCTL1) = 0; #ifdef __MSP430_HAS_TA3__ TA_REG(CCTL2) = 0; #endif TA_REG(CTL) = TACLR + TASSEL_2 + ID_3 + MC_2; // clear counter, source=SMCLK/8, mode=continous count up tone_state = 1; // init is done } // Set the timer interval and duration // frequency in [Hz] and duration in [msec] // we initialize the timer match value only if the tone was not running already, to prevent glitches when re-programming a running tone static void setTimer(uint8_t n, unsigned int frequency, unsigned long duration) { if ( frequency <= 0 ) { tone_interval[n] = 0; tone_periods[n] = 0; return; } tone_interval[n] = F_TIMER / (2L*frequency); if ( duration > 0 ) tone_periods[n] = (duration * (F_TIMER/2)) / (1000L * tone_interval[n]); else tone_periods[n] = -1; switch( n ) // enable IRQ and set next match time in various timer compare registers (if we where not enabled already) { case 0: if ( !(TA_REG(CCTL0) & CCIE) ) TA_REG(CCR0) = TA_REG(R) + tone_interval[0]; TA_REG(CCTL0) = CCIE; break; case 1: if ( !(TA_REG(CCTL1) & CCIE) ) TA_REG(CCR1) = TA_REG(R) + tone_interval[1]; TA_REG(CCTL1) = CCIE; break; #ifdef __MSP430_HAS_TA3__ case 2: if ( !(TA_REG(CCTL2) & CCIE) ) TA_REG(CCR2) = TA_REG(R) + tone_interval[2]; TA_REG(CCTL2) = CCIE; break; #endif } } /* stopTimer() - Disable timer IRQ */ static void inline stopTimer(uint8_t n) { switch( n ) { case 0: TA_REG(CCTL0) = 0; break; case 1: TA_REG(CCTL1) = 0; break; #ifdef __MSP430_HAS_TA3__ case 2: TA_REG(CCTL2) = 0; break; #endif } *tone_out[n] &= ~tone_bit[n]; } // Peform the isr magic, toggle output, decrease duation if > 0, and stop if duration == 0, continous if duration < 0 // set new interval - defined as macro to limit ISR overhead (at the expense of some code size) #define isrTimer(n,ccr) do { \ *tone_out[n] ^= tone_bit[n]; \ if ( tone_periods[n] == 0 ) stopTimer(n);\ else if ( tone_periods[n] > 0) tone_periods[n]--; \ ccr += tone_interval[n]; \ } while(0) // TIMERA vector (CCR0) #if TONE_TIMER == 0 __attribute__((interrupt(TIMER0_A0_VECTOR))) #else __attribute__((interrupt(TIMER1_A0_VECTOR))) #endif void TIMERT_A0_ISR(void) { isrTimer(0, TA_REG(CCR0)); } // TAIV vector (CCR1/CCR2) #if TONE_TIMER == 0 __attribute__((interrupt(TIMER0_A1_VECTOR))) #else __attribute__((interrupt(TIMER1_A1_VECTOR))) #endif void TIMERT_A1_ISR(void) { switch ( TA_REG(IV) ) { case 0x2: isrTimer(1, TA_REG(CCR1)); break; // CCR1 #ifdef __MSP430_HAS_TA3__ case 0x4: isrTimer(2, TA_REG(CCR2)); break; // CCR2 #endif } } On top of your sketch: #define TONE_TIMER 1 jetjaguar 1 Quote Link to post Share on other sites
jetjaguar 0 Posted January 27, 2014 Author Share Posted January 27, 2014 Thank you so much roadrunner84! Unfortunately I get new errors now. After I got them I noticed that you reference, but I did not have, "pins_energia.h", so I added that from here https://github.com/energia/Energia/blob/master/hardware/msp430/variants/launchpad/pins_energia.h . However I still receive errors: C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:90:26: error: 'AVAILABLE_TONE_PINS' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:90:63: error: 'SETARRAY' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:91:25: error: 'AVAILABLE_TONE_PINS' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:91:62: error: 'SETARRAY' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:92:35: error: 'AVAILABLE_TONE_PINS' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:92:70: error: 'SETARRAY' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:93:31: error: 'AVAILABLE_TONE_PINS' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:93:67: error: 'SETARRAY' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:94:29: error: 'AVAILABLE_TONE_PINS' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:94:64: error: 'SETARRAY' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp: In function 'void tone(uint8_t, unsigned int, long unsigned int)': C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:109:23: error: 'AVAILABLE_TONE_PINS' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:111:9: error: 'tone_pins' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:119:23: error: 'AVAILABLE_TONE_PINS' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:121:9: error: 'tone_pins' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:124:7: error: 'tone_bit' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:125:7: error: 'tone_out' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp: In function 'void noTone(uint8_t)': C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:143:23: error: 'AVAILABLE_TONE_PINS' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:145:9: error: 'tone_pins' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp: In function 'void initTimers()': C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:158:1: error: 'TA0TA0CCTL0' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:159:1: error: 'TA0TA0CCTL1' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:161:1: error: 'TA0TA0CCTL2' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp: In function 'void setTimer(uint8_t, unsigned int, long unsigned int)': C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:175:5: error: 'tone_interval' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:176:5: error: 'tone_periods' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:179:3: error: 'tone_interval' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:181:5: error: 'tone_periods' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:183:5: error: 'tone_periods' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:187:1: error: 'TA0TA0CCTL0' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:187:1: error: 'TA0TA0CCR0' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:188:1: error: 'TA0TA0CCTL0' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:191:1: error: 'TA0TA0CCTL1' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:191:1: error: 'TA0TA0CCR1' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:192:1: error: 'TA0TA0CCTL1' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:196:1: error: 'TA0TA0CCTL2' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:196:1: error: 'TA0TA0CCR2' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:197:1: error: 'TA0TA0CCTL2' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp: In function 'void stopTimer(uint8_t)': C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:208:1: error: 'TA0TA0CCTL0' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:209:1: error: 'TA0TA0CCTL1' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:211:1: error: 'TA0TA0CCTL2' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:214:4: error: 'tone_out' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:214:20: error: 'tone_bit' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp: In function 'void TIMERT_A0_ISR()': C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:236:3: error: 'tone_out' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:236:3: error: 'tone_bit' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:236:3: error: 'tone_periods' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:236:3: error: 'TA0TA0CCR0' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:236:3: error: 'tone_interval' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp: In function 'void TIMERT_A1_ISR()': C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:249:15: error: 'tone_out' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:249:15: error: 'tone_bit' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:249:15: error: 'tone_periods' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:249:15: error: 'TA0TA0CCR1' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:249:15: error: 'tone_interval' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:251:15: error: 'tone_out' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:251:15: error: 'tone_bit' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:251:15: error: 'tone_periods' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:251:15: error: 'TA0TA0CCR2' was not declared in this scope C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:251:15: error: 'tone_interval' was not declared in this scope Quote Link to post Share on other sites
jetjaguar 0 Posted January 27, 2014 Author Share Posted January 27, 2014 Maybe I should spell out exactly what I did: I replaced my tone.cpp code with yours. Next, I added #define TONE_TIMER 1 to the top of my sketch (the same one from my opening post). That is all. Quote Link to post Share on other sites
roadrunner84 466 Posted January 28, 2014 Share Posted January 28, 2014 Oh, stupid me! You should replace this part #ifndef TONE_TIMER #define TONE_TIMER 0 #else #if TONE_TIMER == 1 #ifndef __MSP430_HAS_T1A3__ #error Tone: Timer A1 is not available on this microcontroller #endif #ifdef __MSP430_HAS_T1A3__ #define AVAILABLE_TONE_PINS 3 #define SETARRAY(a) a,a,a #else #define AVAILABLE_TONE_PINS 2 #define SETARRAY(a) a,a #endif #elif TONE_TIMER != 0 #error Tone: Do not know how to use a timer other than Timer A0 and Timer A1 #else #ifdef __MSP430_HAS_TA3__ #define AVAILABLE_TONE_PINS 3 #define SETARRAY(a) a,a,a #else #define AVAILABLE_TONE_PINS 2 #define SETARRAY(a) a,a #endif #endif #endif with this part #ifndef TONE_TIMER #define TONE_TIMER 0 #endif #if TONE_TIMER == 1 #ifndef __MSP430_HAS_T1A3__ #error Tone: Timer A1 is not available on this microcontroller #endif #ifdef __MSP430_HAS_T1A3__ #define AVAILABLE_TONE_PINS 3 #define SETARRAY(a) a,a,a #else #define AVAILABLE_TONE_PINS 2 #define SETARRAY(a) a,a #endif #elif TONE_TIMER != 0 #error Tone: Do not know how to use a timer other than Timer A0 and Timer A1 #else #ifdef __MSP430_HAS_TA3__ #define AVAILABLE_TONE_PINS 3 #define SETARRAY(a) a,a,a #else #define AVAILABLE_TONE_PINS 2 #define SETARRAY(a) a,a #endif #endif It looks like Energia compiles Tone separately, sorry. My solution won't work because your define TONE_TIMER is not "seen" by Tone. I'll do a little rewrite on Servo instead... jetjaguar 1 Quote Link to post Share on other sites
roadrunner84 466 Posted January 28, 2014 Share Posted January 28, 2014 How about this for Servo.cpp: /* Servo.cpp - Interrupt driven Servo library for MSP430 Copyright (c) 2012 Petr Baudis. All right reserved. Modified by Peter Brier 26-6-2012: Fixed timing/IRQ problem Modified by Roadrunner84@[member=43oh].com 28-1-2014: Allow use of Timer A1 Derived from: Servo.cpp - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2 Copyright (c) 2009 Michael Margolis. All right reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "Energia.h" #include "Servo.h" #define F_TIMER (F_CPU/8L) #define usToTicks(_us) (( clockCyclesPerMicrosecond()* _us) / 8) // converts microseconds to timer ticks (assumes prescale of 8) #define ticksToUs(_ticks) (( (unsigned)_ticks * 8)/ clockCyclesPerMicrosecond() ) // converts from ticks back to microseconds #define TRIM_DURATION 2 // compensation ticks to trim adjust for digitalWrite delays // 12 August 2009 #define SERVO_MIN() (MIN_PULSE_WIDTH - this->min * 4) // minimum value in uS for this servo #define SERVO_MAX() (MAX_PULSE_WIDTH - this->max * 4) // maximum value in uS for this servo /************ static functions and data structures common to all instances ***********************/ static servo_t servos[MAX_SERVOS]; // static array of servo structures static unsigned int ServoCount = 0; // the total number of attached servos static volatile int counter = 0; // Servo counter; -1 before first servo starts being serviced static volatile unsigned int totalWait = 0; // Total amount waited so far in the current period; after all servos, wait for the rest of REFRESH_INTERVAL #ifndef SERVO_TIMER #define SERVO_TIMER 0 #endif #if SERVO_TIMER == 0 #ifdef TIMERA0_VECTOR #define SERVO_VECTOR TIMERA0_VECTOR #else #define SERVO_VECTOR TIMER0_A0_VECTOR #endif #define TAservoCCTL0 TA0CCTL0 #define TAservoCCR0 TA0CCR0 #define TAservoCTL TA0CTL #elif SERVO_TIMER == 1 #define SERVO_VECTOR TIMER1_A0_VECTOR #define TAservoCCTL0 TA1CCTL0 #define TAservoCCR0 TA1CCR0 #define TAservoCTL TA1CTL #else #error Servo: Do not know how to use a timer other than Timer A0 or Timer A1. #endif // Timer A interrupt service routine static void Timer_A(void) { static unsigned long wait; if (counter >= 0) { /* Turn pulse off. */ digitalWrite(servos[counter].Pin.nbr, LOW); } /* Service next servo, while skipping any inactive servo records. */ do { counter++; /* counter is nonnegative, so it is save to type cast to unsigned */ } while (!servos[counter].Pin.isActive && (unsigned)counter < ServoCount); /* Counter range is 0-ServoCount, the last count is used to complete the REFRESH_INTERVAL * counter is nonnegative, so it is save to type cast to unsigned */ if ((unsigned)counter < ServoCount) { /* Turn pulse on for the next servo. */ digitalWrite(servos[counter].Pin.nbr, HIGH); /* And hold! */ totalWait += servos[counter].ticks; TAservoCCR0 = servos[counter].ticks; } else { /* Wait for the remaining of REFRESH_INTERVAL. */ wait = usToTicks(REFRESH_INTERVAL) - totalWait; totalWait = 0; TAservoCCR0 = (wait < 1000 ? 1000 : wait); counter = -1; } } // Timer A interrupt service routine __attribute__((interrupt(SERVO_VECTOR))) static void Timer_A_int(void) { Timer_A(); } static boolean isTimerActive(void) { // returns true if any servo is active for(int i = 0; i < MAX_SERVOS; i++) if (servos[i].Pin.isActive == true) return true; return false; } static void enableTimer(void) { counter = -1; totalWait = 0; Timer_A(); // enable first servo TAservoCCTL0 = CCIE; // CCR0 interrupt enabled TAservoCTL = TASSEL_2 + MC_1 + ID_3; // prescale SMCLK/8, upmode } static void disableTimer(void) { // disable interrupt TAservoCCTL0 = 0; TAservoCCR0 = 0; } /****************** end of static functions ******************************/ Servo::Servo() { if( ServoCount < MAX_SERVOS) { this->servoIndex = ServoCount++; // assign a servo index to this instance servos[this->servoIndex].ticks = usToTicks(DEFAULT_PULSE_WIDTH); // store default values } else this->servoIndex = INVALID_SERVO ; // too many servos } uint8_t Servo::attach(int pin) { return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH); } uint8_t Servo::attach(int pin, int min, int max) { if(this->servoIndex < MAX_SERVOS ) { pinMode( pin, OUTPUT) ; // set servo pin to output servos[this->servoIndex].Pin.nbr = pin; // todo min/max check: abs(min - MIN_PULSE_WIDTH) /4 < 128 this->min = (MIN_PULSE_WIDTH - min)/4; //resolution of min/max is 4 uS this->max = (MAX_PULSE_WIDTH - max)/4; boolean timer_active = isTimerActive(); servos[this->servoIndex].Pin.isActive = true; if (!timer_active) enableTimer(); } return this->servoIndex ; } void Servo::detach() { servos[this->servoIndex].Pin.isActive = false; if (!isTimerActive()) disableTimer(); } void Servo::write(int value) { if(value < MIN_PULSE_WIDTH) { // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds) if(value < 0) value = 0; if(value > 180) value = 180; value = map(value, 0, 180, SERVO_MIN(), SERVO_MAX()); } this->writeMicroseconds(value); } void Servo::writeMicroseconds(int value) { // calculate and store the values for the given channel byte channel = this->servoIndex; if( (channel < MAX_SERVOS) ) // ensure channel is valid { if( value < SERVO_MIN() ) // ensure pulse width is valid value = SERVO_MIN(); else if( value > SERVO_MAX() ) value = SERVO_MAX(); value = value - TRIM_DURATION; volatile int v = usToTicks(value); // convert to ticks after compensating for interrupt overhead - 12 Aug 2009 servos[channel].ticks = v; // this is atomic on a 16bit uC, no need to disable Interrupts } } int Servo::read() // return the value as degrees { return map( this->readMicroseconds()+1, SERVO_MIN(), SERVO_MAX(), 0, 180); } int Servo::readMicroseconds() { unsigned int pulsewidth; if( this->servoIndex != INVALID_SERVO ) pulsewidth = ticksToUs(servos[this->servoIndex].ticks) + TRIM_DURATION; else pulsewidth = 0; return pulsewidth; } bool Servo::attached() { return servos[this->servoIndex].Pin.isActive ; } Before the include, put a define: #define SERVO_TIMER 1 #include <Servo.h> energia 1 Quote Link to post Share on other sites
jetjaguar 0 Posted January 28, 2014 Author Share Posted January 28, 2014 I installed Energia in a new location because I've monkeyed with my libraries so much that I don't trust them. After doing that I replaced tone.cpp with your code from post #2. Then I replaced some of the code as instructed from your post #5. Then I replaced my servo.cpp code with yours and made sure sure to add #define SERVO_TIMER 1 to the top of the sketch. Here are the new errors: C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp, -o, C:\Users\VisLab\AppData\Local\Temp\build312496893865346441.tmp\Tone.cpp.o] C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp: In function 'void initTimers()': C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:157:1: error: 'TA0TA0CCTL0' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:158:1: error: 'TA0TA0CCTL1' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:160:1: error: 'TA0TA0CCTL2' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp: In function 'void setTimer(uint8_t, unsigned int, long unsigned int)': C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:186:1: error: 'TA0TA0CCTL0' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:186:1: error: 'TA0TA0CCR0' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:187:1: error: 'TA0TA0CCTL0' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:190:1: error: 'TA0TA0CCTL1' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:190:1: error: 'TA0TA0CCR1' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:191:1: error: 'TA0TA0CCTL1' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:195:1: error: 'TA0TA0CCTL2' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:195:1: error: 'TA0TA0CCR2' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:196:1: error: 'TA0TA0CCTL2' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp: In function 'void stopTimer(uint8_t)': C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:207:1: error: 'TA0TA0CCTL0' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:208:1: error: 'TA0TA0CCTL1' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:210:1: error: 'TA0TA0CCTL2' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp: In function 'void TIMERT_A0_ISR()': C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:235:3: error: 'TA0TA0CCR0' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp: In function 'void TIMERT_A1_ISR()': C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:248:15: error: 'TA0TA0CCR1' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:250:15: error: 'TA0TA0CCR2' was not declared in this scope The errors confuse me because, for example, the first one mentions "TA0TA0CCTL0" in Tone.cpp, but that doesn't exist anywhere in Tone.cpp... Quote Link to post Share on other sites
roadrunner84 466 Posted January 29, 2014 Share Posted January 29, 2014 You should leave the Tone.cpp untouched, my "patch" isn't working because of how Tone is linked to your sketch. Quote Link to post Share on other sites
grahamf72 169 Posted January 29, 2014 Share Posted January 29, 2014 The reason you are getting errors that look like the following: C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:207:1: error: 'TA0TA0CCTL0' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:208:1: error: 'TA0TA0CCTL1' was not declared in this scope C:\TiLaunchpad_startfresh\energia-0101E0011\hardware\msp430\cores\msp430\Tone.cpp:210:1: error: 'TA0TA0CCTL2' was not declared in this scope is because of a bug in a little trick that the tone.cpp file is doing to allow you to specify the different timer numbers. The tone.cpp code posted above has the following macros: #define _TONE_CONCAT(c, r) TA ## c ## r #define _TONE_RESOLVE(l, r) _TONE_CONCAT(l, r) #define TA_REG(name) _TONE_RESOLVE(TONE_TIMER, name) The idea of these macros is that you define TONE_TIMER to be either 1 or 0, then the code uses the macro TA_REG(register name) to automatically choose the appropriate TA0xxx or a TA1xxx register. So for example if TONE_TIMER is 0, TA_REG(CCTL0) will expand out to _TONE_RESOLVE(TONE_TIMER,CCTL0). This in turn expands out to _TONE_CONCAT(0,CCTL0), which will expand out to TA0CCTL0. Well at least that's how it's supposed to work. Unfortunately, the msp430g2553.h file has the following #defines... /* Alternate register names */ #define TAIV TA0IV /* Timer A Interrupt Vector Word */ #define TACTL TA0CTL /* Timer A Control */ #define TACCTL0 TA0CCTL0 /* Timer A Capture/Compare Control 0 */ #define TACCTL1 TA0CCTL1 /* Timer A Capture/Compare Control 1 */ #define TACCTL2 TA0CCTL2 /* Timer A Capture/Compare Control 2 */ #define TAR TA0R /* Timer A */ #define TACCR0 TA0CCR0 /* Timer A Capture/Compare 0 */ #define TACCR1 TA0CCR1 /* Timer A Capture/Compare 1 */ #define TACCR2 TA0CCR2 /* Timer A Capture/Compare 2 */ #define TAIV_ TA0IV_ /* Timer A Interrupt Vector Word */ #define TACTL_ TA0CTL_ /* Timer A Control */ #define TACCTL0_ TA0CCTL0_ /* Timer A Capture/Compare Control 0 */ #define TACCTL1_ TA0CCTL1_ /* Timer A Capture/Compare Control 1 */ #define TACCTL2_ TA0CCTL2_ /* Timer A Capture/Compare Control 2 */ #define TAR_ TA0R_ /* Timer A */ #define TACCR0_ TA0CCR0_ /* Timer A Capture/Compare 0 */ #define TACCR1_ TA0CCR1_ /* Timer A Capture/Compare 1 */ #define TACCR2_ TA0CCR2_ /* Timer A Capture/Compare 2 */ /* Alternate register names 2 */ #define CCTL0 TACCTL0 /* Timer A Capture/Compare Control 0 */ #define CCTL1 TACCTL1 /* Timer A Capture/Compare Control 1 */ #define CCTL2 TACCTL2 /* Timer A Capture/Compare Control 2 */ #define CCR0 TACCR0 /* Timer A Capture/Compare 0 */ #define CCR1 TACCR1 /* Timer A Capture/Compare 1 */ #define CCR2 TACCR2 /* Timer A Capture/Compare 2 */ #define CCTL0_ TACCTL0_ /* Timer A Capture/Compare Control 0 */ #define CCTL1_ TACCTL1_ /* Timer A Capture/Compare Control 1 */ #define CCTL2_ TACCTL2_ /* Timer A Capture/Compare Control 2 */ #define CCR0_ TACCR0_ /* Timer A Capture/Compare 0 */ #define CCR1_ TACCR1_ /* Timer A Capture/Compare 1 */ #define CCR2_ TACCR2_ /* Timer A Capture/Compare 2 */ So when we declare TA_REG(CCTL0), instead of concatenating CCTL0 to TA1 to make TA1CCTL0 like it is supposed to, you'll notice that CCTL0 is defined to be TACCTL0. TACCTL0 is in return defined to be TA0CCTL0. So instead of TA_REG(CCTL0) expanding to _TONE_RESOLVE(TONE_TIMER,CCTL0) as it is supposed to, it actually expands out to _TONE_RESOLVE(TONE_TIMER,TA0CCTL0). Moving up the #defines, this ultimately expands out to TA0TA0CCTL0 which gives an error because it is not defined. The solution... In use roadrunners version of tone.cpp in post #2, with the fixes in post #5 then after the #include's & local function definitions, but before the line #define F_TIMER...., put the following: #undef CCTL0 #undef CCTL1 #undef CCTL2 #undef CCR0 #undef CCR1 #undef CCR2 #define TONE_TIMER 1 The #undef's will get rid of the extra definitions that msp430g2553.h provides, so that the macros in tone.cpp will work properly. Also because Energia doesn't provide a way to pass compiler pre-defines, you need to explicitly set the value of TONE_TIMER in tone.cpp. If you want to use Timer 0 you will need to change to a 0 in the above line. energia 1 Quote Link to post Share on other sites
roadrunner84 466 Posted January 29, 2014 Share Posted January 29, 2014 @@grahamf72 I see, I overlooked that. In addition, as I said, the linker disallows defines to change defines before compiling Tone.cpp. So instead of modifying Tone.cpp you should modify Servo.cpp. Quote Link to post Share on other sites
jetjaguar 0 Posted January 29, 2014 Author Share Posted January 29, 2014 Thank you for that great in depth explanation grahamf72! Whether I alter tone.cpp the way you suggest, or the way roadrunner84 suggests, or incorporating both of your changes into the file, I still get the same error as if I hadn't changed it at all. I've also tried compiling with and without the servo.cpp mod and the error is still the same. here is my servo.cpp: http://pastebin.com/vspia1et here is my tone.cpp: http://pastebin.com/VdtQV4f4 This is the sketch I am compiling (Servo.h & pitches.h are tabs in the sketch): #define SERVO_TIMER 1 #define TONE_TIMER 1 #include <Servo.h> //below is simply the toneMelody example #include "pitches.h" // notes in the melody: int melody[] = { NOTE_C4, NOTE_G3,NOTE_G3, NOTE_A3, NOTE_G3,0, NOTE_B3, NOTE_C4}; // note durations: 4 = quarter note, 8 = eighth note, etc.: int noteDurations[] = { 4, 8, 8, 4,4,4,4,4 }; void setup() { // iterate over the notes of the melody: for (int thisNote = 0; thisNote < 8; thisNote++) { // to calculate the note duration, take one second // divided by the note type. //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc. int noteDuration = 1000/noteDurations[thisNote]; tone(8, melody[thisNote],noteDuration); // to distinguish the notes, set a minimum time between them. // the note's duration + 30% seems to work well: int pauseBetweenNotes = noteDuration * 1.30; delay(pauseBetweenNotes); // stop the tone playing: noTone(8); } } void loop() { // no need to repeat the melody. } errors: core.a(Tone.cpp.o): In function `TIMER0_A0_ISR()': C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\cores\msp430/Tone.cpp:208: multiple definition of `__isr_9' Servo\Servo.cpp.o:C:\TiLaunchpad\energia-0101E0011-windows\energia-0101E0011\hardware\msp430\libraries\Servo/Servo.cpp:108: first defined here collect2: ld returned 1 exit status Quote Link to post Share on other sites
grahamf72 169 Posted January 29, 2014 Share Posted January 29, 2014 Using your code, the standard Energia servo library, and the modified tone.cpp I was able to get the code to compile. I haven't tested it on hardware but it does compile ok. It doesn't matter if you modify tone.cpp or servo.cpp, but one (not both) needs to be altered to use the other timer. I haven't tested roadrunner's modified servo.cpp, but using it with the standard Energia tone should also work just as well. Doing things like #define SERVO_TIMER 1 in your code does nothing because all the Energia libraries and cores are compiled separately, and #defines are interpreted at compile time, not link time. Anything that you define in your sketch is only applicable to the code you write, and isn't used when compiling the library or the core. The only way to configure the build options of a file in the library or the core is to manually edit the applicable file(s). If you end up using roadrunner's modified servo.cpp, and the original Energia tone.cpp, you will need to put #define SERVO_TIMER 1 at the top of servo.cpp, not at the top of your sketch. The full compiling (compiles ok, not tested on hardware though) tone.cpp that I have is as follows: /* Tone.cpp A Tone Generator Library - Modified for Energia Implements up to 3 (software) PWM outputs using TIMERA0 compare registers and IRQ. Can use any digital output pin for pulse generation (c) 2012 - Peter Brier. Based on code Originally written by Brett Hagman This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Version Modified By Date Comments ------- ----------- -------- -------- 0001 B Hagman 09/08/02 Initial coding 0002 B Hagman 09/08/18 Multiple pins 0003 B Hagman 09/08/18 Moved initialization from constructor to begin() 0004 B Hagman 09/09/26 Fixed problems with ATmega8 0005 B Hagman 09/11/23 Scanned prescalars for best fit on 8 bit timers 09/11/25 Changed pin toggle method to XOR 09/11/25 Fixed timer0 from being excluded 0006 D Mellis 09/12/29 Replaced objects with functions 0007 M Sproul 10/08/29 Changed #ifdefs from cpu to register 0008 P Brier 12/05/28 Modified for TI MSP430 processor 0009 P Brier 12/05/29 Fixed problem with re-init of expired tone *************************************************/ #include "wiring_private.h" #include "pins_energia.h" #include "Energia.h" // local funcions static void initTimers(); static void setTimer(uint8_t n, unsigned int frequency, unsigned long duration); static void stopTimer(uint8_t n); #undef CCTL0 #undef CCTL1 #undef CCTL2 #undef CCR0 #undef CCR1 #undef CCR2 #define TONE_TIMER 1 // timer clock frequency set to clock/8, at F_CPU = 1MHZ this gives an output freq range of ~[1Hz ..65Khz] and at 16Mhz this is ~[16Hz .. 1MHz] #define F_TIMER (F_CPU/8L) #ifndef TONE_TIMER #define TONE_TIMER 0 #endif #if TONE_TIMER == 1 #ifndef __MSP430_HAS_T1A3__ #error Tone: Timer A1 is not available on this microcontroller #endif #ifdef __MSP430_HAS_T1A3__ #define AVAILABLE_TONE_PINS 3 #define SETARRAY(a) a,a,a #else #define AVAILABLE_TONE_PINS 2 #define SETARRAY(a) a,a #endif #elif TONE_TIMER != 0 #error Tone: Do not know how to use a timer other than Timer A0 and Timer A1 #else #ifdef __MSP430_HAS_TA3__ #define AVAILABLE_TONE_PINS 3 #define SETARRAY(a) a,a,a #else #define AVAILABLE_TONE_PINS 2 #define SETARRAY(a) a,a #endif #endif /* some pre-processor magic to allow TA_REG(CCR) to expand to TA0_CCR for TONE_TIMER == 0 */ #define _TONE_CONCAT(c, r) TA ## c ## r #define _TONE_RESOLVE(l, r) _TONE_CONCAT(l, r) #define TA_REG(name) _TONE_RESOLVE(TONE_TIMER, name) // tone_duration: // > 0 - duration specified // = 0 - stopped // < 0 - infinitely (until stop() method called, or new play() called) static uint8_t tone_state = 0; // 0==not initialized, 1==timer running static uint8_t tone_pins[AVAILABLE_TONE_PINS] = { SETARRAY(255) }; static uint8_t tone_bit[AVAILABLE_TONE_PINS] = { SETARRAY(255) }; volatile static uint8_t *tone_out[AVAILABLE_TONE_PINS] = { SETARRAY(0) }; static uint16_t tone_interval[AVAILABLE_TONE_PINS] = { SETARRAY(-1) }; static int16_t tone_periods[AVAILABLE_TONE_PINS] = { SETARRAY(0) }; /** *** tone() -- Output a tone (50% Dutycycle PWM signal) on a pin *** pin: This pin is selected as output *** frequency: [Hertz] ** duration: [milliseconds], if duration <=0, then we output tone continously, otherwise tone is stopped after this time (output = 0) **/ void tone(uint8_t _pin, unsigned int frequency, unsigned long duration) { uint8_t port = digitalPinToPort(_pin); if (port == NOT_A_PORT) return; // find if we are using it at the moment, if so: update it for (int i = 0; i < AVAILABLE_TONE_PINS; i++) { if (tone_pins[i] == _pin) { setTimer(i, frequency, duration); return; // we are done, timer reprogrammed } } // new tone pin, find empty timer and set it for (int i = 0; i < AVAILABLE_TONE_PINS; i++) { if (tone_pins[i] == 255) { tone_pins[i] = _pin; tone_bit[i] = digitalPinToBitMask(_pin); tone_out[i] = portOutputRegister(port); if ( tone_state == 0 ) initTimers(); pinMode(_pin, OUTPUT); setTimer(i, frequency, duration); return; // we are done, timer set } } // if we exit here, no unused timer was found, nothing is done } /** *** noTone() - Stop outputting the tone on a pin **/ void noTone(uint8_t _pin) { if ( _pin == 255 ) return; // Should not happen! for (int i = 0; i < AVAILABLE_TONE_PINS; i++) { if (tone_pins[i] == _pin) { tone_pins[i] = 255; stopTimer(i); } } } // Initialize the timers - Set mode and Enable IRQ static void inline initTimers() { // disable IRQs TA_REG(CCTL0) = 0; TA_REG(CCTL1) = 0; #ifdef __MSP430_HAS_TA3__ TA_REG(CCTL2) = 0; #endif TA_REG(CTL) = TACLR + TASSEL_2 + ID_3 + MC_2; // clear counter, source=SMCLK/8, mode=continous count up tone_state = 1; // init is done } // Set the timer interval and duration // frequency in [Hz] and duration in [msec] // we initialize the timer match value only if the tone was not running already, to prevent glitches when re-programming a running tone static void setTimer(uint8_t n, unsigned int frequency, unsigned long duration) { if ( frequency <= 0 ) { tone_interval[n] = 0; tone_periods[n] = 0; return; } tone_interval[n] = F_TIMER / (2L*frequency); if ( duration > 0 ) tone_periods[n] = (duration * (F_TIMER/2)) / (1000L * tone_interval[n]); else tone_periods[n] = -1; switch( n ) // enable IRQ and set next match time in various timer compare registers (if we where not enabled already) { case 0: if ( !(TA_REG(CCTL0) & CCIE) ) TA_REG(CCR0) = TA_REG(R) + tone_interval[0]; TA_REG(CCTL0) = CCIE; break; case 1: if ( !(TA_REG(CCTL1) & CCIE) ) TA_REG(CCR1) = TA_REG(R) + tone_interval[1]; TA_REG(CCTL1) = CCIE; break; #ifdef __MSP430_HAS_TA3__ case 2: if ( !(TA_REG(CCTL2) & CCIE) ) TA_REG(CCR2) = TA_REG(R) + tone_interval[2]; TA_REG(CCTL2) = CCIE; break; #endif } } /* stopTimer() - Disable timer IRQ */ static void inline stopTimer(uint8_t n) { switch( n ) { case 0: TA_REG(CCTL0) = 0; break; case 1: TA_REG(CCTL1) = 0; break; #ifdef __MSP430_HAS_TA3__ case 2: TA_REG(CCTL2) = 0; break; #endif } *tone_out[n] &= ~tone_bit[n]; } // Peform the isr magic, toggle output, decrease duation if > 0, and stop if duration == 0, continous if duration < 0 // set new interval - defined as macro to limit ISR overhead (at the expense of some code size) #define isrTimer(n,ccr) do { \ *tone_out[n] ^= tone_bit[n]; \ if ( tone_periods[n] == 0 ) stopTimer(n);\ else if ( tone_periods[n] > 0) tone_periods[n]--; \ ccr += tone_interval[n]; \ } while(0) // TIMERA vector (CCR0) #if TONE_TIMER == 0 __attribute__((interrupt(TIMER0_A0_VECTOR))) #else __attribute__((interrupt(TIMER1_A0_VECTOR))) #endif void TIMERT_A0_ISR(void) { isrTimer(0, TA_REG(CCR0)); } // TAIV vector (CCR1/CCR2) #if TONE_TIMER == 0 __attribute__((interrupt(TIMER0_A1_VECTOR))) #else __attribute__((interrupt(TIMER1_A1_VECTOR))) #endif void TIMERT_A1_ISR(void) { switch ( TA_REG(IV) ) { case 0x2: isrTimer(1, TA_REG(CCR1)); break; // CCR1 #ifdef __MSP430_HAS_TA3__ case 0x4: isrTimer(2, TA_REG(CCR2)); break; // CCR2 #endif } } energia and jetjaguar 2 Quote Link to post Share on other sites
jetjaguar 0 Posted January 30, 2014 Author Share Posted January 30, 2014 Thank you both very much, it works now! I was able to replicate what grahamf72 suggested and the robot can now sing. I really appreciate all the help you both gave me, I believe I understand most of your explanations. Once it is done I'll post it somewhere on this forum. Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.