Jump to content
jetjaguar

conflicting timers - using Servo.h and pitches.h

Recommended Posts

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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...

Share this post


Link to post
Share on other sites

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>

Share this post


Link to post
Share on other sites

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...

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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
  }  
}

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×