Advertisement
Guest User

tone.cpp

a guest
Jan 29th, 2014
98
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 8.23 KB | None | 0 0
  1.  
  2.  
  3. //this code is modified by MLF based on
  4. // http://forum.43oh.com/topic/4981-conflicting-timers-using-servoh-and-pitchesh/
  5.  
  6.  
  7.  
  8.  
  9. /* Tone.cpp
  10.  
  11.   A Tone Generator Library - Modified for Energia
  12.   Implements up to 3 (software) PWM outputs using TIMERA0 compare registers and IRQ.
  13.   Can use any digital output pin for pulse generation
  14.  
  15.   (c) 2012 - Peter Brier.
  16.  
  17.   Based on code Originally written by Brett Hagman
  18.  
  19.   This library is free software; you can redistribute it and/or
  20.   modify it under the terms of the GNU Lesser General Public
  21.   License as published by the Free Software Foundation; either
  22.   version 2.1 of the License, or (at your option) any later version.
  23.  
  24.   This library is distributed in the hope that it will be useful,
  25.   but WITHOUT ANY WARRANTY; without even the implied warranty of
  26.   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  27.   Lesser General Public License for more details.
  28.  
  29.   You should have received a copy of the GNU Lesser General Public
  30.   License along with this library; if not, write to the Free Software
  31.   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  32.  
  33. Version Modified By Date     Comments
  34. ------- ----------- -------- --------
  35. 0001    B Hagman    09/08/02 Initial coding
  36. 0002    B Hagman    09/08/18 Multiple pins
  37. 0003    B Hagman    09/08/18 Moved initialization from constructor to begin()
  38. 0004    B Hagman    09/09/26 Fixed problems with ATmega8
  39. 0005    B Hagman    09/11/23 Scanned prescalars for best fit on 8 bit timers
  40.                     09/11/25 Changed pin toggle method to XOR
  41.                     09/11/25 Fixed timer0 from being excluded
  42. 0006    D Mellis    09/12/29 Replaced objects with functions
  43. 0007    M Sproul    10/08/29 Changed #ifdefs from cpu to register
  44. 0008    P Brier     12/05/28 Modified for TI MSP430 processor
  45. 0009    P Brier     12/05/29 Fixed problem with re-init of expired tone
  46. *************************************************/
  47.  
  48. #include "wiring_private.h"
  49. #include "pins_energia.h"
  50. #include "Energia.h"
  51.  
  52. // local funcions
  53. static void initTimers();
  54. static void setTimer(uint8_t n, unsigned int frequency, unsigned long duration);
  55. static void stopTimer(uint8_t n);
  56.  
  57. /////
  58. #undef CCTL0
  59. #undef CCTL1
  60. #undef CCTL2
  61. #undef CCR0
  62. #undef CCR1
  63. #undef CCR2
  64.  
  65. #define TONE_TIMER 1
  66. ///////////
  67.  
  68.  
  69. // 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]
  70. #define F_TIMER (F_CPU/8L)
  71.  
  72. #ifndef TONE_TIMER
  73.   #define TONE_TIMER 0
  74. #endif
  75. #if TONE_TIMER == 1
  76.   #ifndef __MSP430_HAS_T1A3__
  77.     #error Tone: Timer A1 is not available on this microcontroller
  78.   #endif
  79.     #ifdef __MSP430_HAS_T1A3__
  80.       #define AVAILABLE_TONE_PINS 3
  81.       #define SETARRAY(a) a,a,a
  82.     #else
  83.       #define AVAILABLE_TONE_PINS 2
  84.       #define SETARRAY(a) a,a
  85.     #endif
  86. #elif TONE_TIMER != 0
  87.   #error Tone: Do not know how to use a timer other than Timer A0 and Timer A1
  88. #else
  89.   #ifdef __MSP430_HAS_TA3__
  90.     #define AVAILABLE_TONE_PINS 3
  91.     #define SETARRAY(a) a,a,a
  92.   #else
  93.     #define AVAILABLE_TONE_PINS 2
  94.     #define SETARRAY(a) a,a
  95.   #endif
  96. #endif
  97.  
  98. /* some pre-processor magic to allow TA_REG(CCR) to expand to TA0_CCR for TONE_TIMER == 0 */
  99. #define _TONE_CONCAT(c, r) TA ## c ## r
  100. #define _TONE_RESOLVE(l, r) _TONE_CONCAT(l, r)
  101. #define TA_REG(name) _TONE_RESOLVE(TONE_TIMER, name)
  102.  
  103. // tone_duration:
  104. //  > 0 - duration specified
  105. //  = 0 - stopped
  106. //  < 0 - infinitely (until stop() method called, or new play() called)
  107.  
  108. static uint8_t tone_state = 0; // 0==not initialized, 1==timer running
  109. static uint8_t tone_pins[AVAILABLE_TONE_PINS] = { SETARRAY(255) };
  110. static uint8_t tone_bit[AVAILABLE_TONE_PINS] = { SETARRAY(255)  };
  111. volatile static uint8_t *tone_out[AVAILABLE_TONE_PINS] = { SETARRAY(0) };
  112. static uint16_t tone_interval[AVAILABLE_TONE_PINS] = { SETARRAY(-1)  };
  113. static int16_t tone_periods[AVAILABLE_TONE_PINS] = { SETARRAY(0)  };
  114.  
  115.  
  116. /**
  117. *** tone() -- Output a tone (50% Dutycycle PWM signal) on a pin
  118. ***  pin: This pin is selected as output
  119. ***  frequency: [Hertz]
  120. **   duration: [milliseconds], if duration <=0, then we output tone continously, otherwise tone is stopped after this time (output = 0)
  121. **/
  122. void tone(uint8_t _pin, unsigned int frequency, unsigned long duration)
  123. {
  124.   uint8_t port = digitalPinToPort(_pin);
  125.   if (port == NOT_A_PORT) return;
  126.  
  127.   // find if we are using it at the moment, if so: update it
  128.   for (int i = 0; i < AVAILABLE_TONE_PINS; i++)
  129.   {
  130.     if (tone_pins[i] == _pin)
  131.     {
  132.       setTimer(i, frequency, duration);
  133.       return; // we are done, timer reprogrammed
  134.     }
  135.   }
  136.  
  137.   // new tone pin, find empty timer and set it
  138.   for (int i = 0; i < AVAILABLE_TONE_PINS; i++)
  139.   {
  140.     if (tone_pins[i] == 255)      
  141.     {
  142.       tone_pins[i] = _pin;
  143.       tone_bit[i] = digitalPinToBitMask(_pin);
  144.       tone_out[i] = portOutputRegister(port);
  145.       if ( tone_state == 0 )
  146.         initTimers();
  147.       pinMode(_pin, OUTPUT);
  148.       setTimer(i, frequency, duration);
  149.       return; // we are done, timer set
  150.     }
  151.   }
  152.   // if we exit here, no unused timer was found, nothing is done
  153. }
  154.  
  155.  
  156. /**
  157. *** noTone() - Stop outputting the tone on a pin
  158. **/
  159. void noTone(uint8_t _pin)
  160. {
  161.   if ( _pin == 255 ) return; // Should not happen!
  162.   for (int i = 0; i < AVAILABLE_TONE_PINS; i++)
  163.   {
  164.     if (tone_pins[i] == _pin)
  165.     {
  166.       tone_pins[i] = 255;
  167.       stopTimer(i);
  168.     }
  169.   }
  170. }
  171.  
  172.  
  173. // Initialize the timers - Set mode and Enable IRQ
  174. static void inline initTimers()
  175. {
  176.   // disable IRQs
  177.   TA_REG(CCTL0) = 0;
  178.   TA_REG(CCTL1) = 0;
  179. #ifdef __MSP430_HAS_TA3__
  180.   TA_REG(CCTL2) = 0;
  181. #endif
  182.   TA_REG(CTL) = TACLR + TASSEL_2 +  ID_3 + MC_2;       // clear counter, source=SMCLK/8, mode=continous count up
  183.   tone_state = 1;  // init is done
  184. }
  185.  
  186.  
  187. // Set the timer interval and duration
  188. // frequency in [Hz] and duration in [msec]
  189. // we initialize the timer match value only if the tone was not running already, to prevent glitches when re-programming a running tone
  190. static void setTimer(uint8_t n, unsigned int frequency, unsigned long duration)
  191. {
  192.   if ( frequency <= 0 )
  193.   {
  194.     tone_interval[n] = 0;
  195.     tone_periods[n] = 0;
  196.     return;
  197.   }
  198.   tone_interval[n] = F_TIMER / (2L*frequency);
  199.   if ( duration > 0 )
  200.     tone_periods[n] = (duration * (F_TIMER/2)) / (1000L * tone_interval[n]);
  201.   else
  202.     tone_periods[n] = -1;
  203.   switch( n ) // enable IRQ and set next match time in various timer compare registers (if we where not enabled already)
  204.   {
  205.     case 0:
  206.       if ( !(TA_REG(CCTL0) & CCIE) ) TA_REG(CCR0) = TA_REG(R) + tone_interval[0];  
  207.       TA_REG(CCTL0) = CCIE;      
  208.       break;
  209.     case 1:
  210.       if ( !(TA_REG(CCTL1) & CCIE) ) TA_REG(CCR1) = TA_REG(R) + tone_interval[1];
  211.       TA_REG(CCTL1) = CCIE;
  212.       break;
  213. #ifdef __MSP430_HAS_TA3__
  214.     case 2:
  215.       if ( !(TA_REG(CCTL2) & CCIE) ) TA_REG(CCR2) = TA_REG(R) + tone_interval[2];  
  216.       TA_REG(CCTL2) = CCIE;
  217.       break;
  218. #endif
  219.     }
  220. }
  221.  
  222. /* stopTimer() - Disable timer IRQ */
  223. static void inline stopTimer(uint8_t n)
  224. {
  225.   switch( n )
  226.   {
  227.     case 0: TA_REG(CCTL0) = 0; break;
  228.     case 1: TA_REG(CCTL1) = 0; break;
  229. #ifdef __MSP430_HAS_TA3__
  230.     case 2: TA_REG(CCTL2) = 0; break;
  231. #endif
  232.   }  
  233.   *tone_out[n] &= ~tone_bit[n];
  234. }
  235.  
  236.  
  237. // Peform the isr magic, toggle output, decrease duation if > 0, and stop if duration == 0, continous if duration < 0
  238. // set new interval - defined as macro to limit ISR overhead (at the expense of some code size)
  239. #define isrTimer(n,ccr) do { \
  240.   *tone_out[n] ^= tone_bit[n]; \
  241.   if ( tone_periods[n] == 0 ) stopTimer(n);\
  242.   else if ( tone_periods[n] > 0) tone_periods[n]--; \
  243.   ccr += tone_interval[n]; \
  244. } while(0)
  245.  
  246.  
  247. // TIMERA vector (CCR0)
  248. #if TONE_TIMER == 0
  249. __attribute__((interrupt(TIMER0_A0_VECTOR)))
  250. #else
  251. __attribute__((interrupt(TIMER1_A0_VECTOR)))
  252. #endif
  253. void TIMERT_A0_ISR(void)
  254. {
  255.   isrTimer(0, TA_REG(CCR0));
  256. }
  257.  
  258. // TAIV vector (CCR1/CCR2)
  259. #if TONE_TIMER == 0
  260. __attribute__((interrupt(TIMER0_A1_VECTOR)))
  261. #else
  262. __attribute__((interrupt(TIMER1_A1_VECTOR)))
  263. #endif
  264. void TIMERT_A1_ISR(void)
  265. {
  266.   switch ( TA_REG(IV) )
  267.   {
  268.     case 0x2: isrTimer(1, TA_REG(CCR1)); break; // CCR1
  269. #ifdef __MSP430_HAS_TA3__
  270.     case 0x4: isrTimer(2, TA_REG(CCR2)); break; // CCR2
  271. #endif
  272.   }  
  273. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement