MSP430 Sound und Musik erzeugen (Tracker)

msp430_music_trackerAnfang letzten Monat habe ich mir mal ein paar Gedanken gemacht zu dem Thema: Sound, Musik und Töne auf dem MSP430 generieren. Da es in diesem Bereich ziemlich viele verschiedene Möglichkeiten gibt, Musik zu erzeugen, habe ich ungefähr eine ganze Woche für das folgende Sound-Modul gebraucht. Der Algorithmus sollte so klein wie möglich bleiben und natürlich sollte die Musik im Hintergrund automatisch abgespielt werden, ohne manuell eine Funktion aufrufen zu müssen. Das bedeutet also: Ein Interrupt muss dafür sorgen, dass die Noten automatisch abgespielt werden. Weiterhin sollte es so einfach wie möglich aufgebaut werden. Also habe ich mich dazu entschlossen, Töne per PWM (Pulsweitenmodulation) zu erzeugen. So wird es auch beim Game Boy Advance realisiert (als grobes Beispiel). Als Grundlage habe ich den typischen Tracker-Stil (auch Rastersequenzer), benutzt bzw. habe dies als Basis genommen. Die Code-Größe bleibt somit ziemlich klein. Zum Einsatz kommt ein MSP430G2553 (wie so oft in letzter Zeit) der zwei Timer besitzt.

Video

Schaltung

Einen Lautsprecher sollte man nicht direkt an einen Port-Ausgang von einem MSP430 anschließen, da die Leistung von einem Port absolut nicht ausreichend ist. Also muss eine Verstärkerschaltung / Treiberstufe benutzt werden. Ich habe eine ganze einfache Gegentaktendstufe oder auch Komplementärendstufe benutzt. Diese ist in diesem Beispiel sehr einfach aufgebaut und reicht für kleine Spielerein. Hier kann man natürlich noch unglaublich viel tunen und modifizieren und sich auch wunderbar einen abrechnen, wenn man Lust hat. Weitere Infos unter Klangerzeugung (Mikrocontroller.net).

msp430_music_tracker_schematic

Tonerzeugung

Wie schon weiter oben geschrieben, kommt ein MSP430G2553 zum Einsatz. Dieser besitzt insgesamt zwei 16bit-Timer. Ein Timer erzeugt per Hardware die Noten-Frequenz (PWM) und der andere Timer ist für die BPM (Beats per Minute) und das Abspielen der Noten zuständig. Das Noten-Update erfolgt in einer Interrupt-Service-Routine (ISR). DIe ISR sollte natürlich so schnell wie möglich abgearbeitet werden um anderen Berechnungen und Abläufe nicht zu lange zu unterbrechen. Wie man in dem Video sehen kann, wird nebenbei noch ein kleiner rotierender 3D-Dot-Cube erzeugt, der auf die Noten reagiert. Wie man mit dem MSP430 eine PWM (Pulsweitenmodulation) erzeugt, habe ich vor ein paar Monaten beschrieben: MSP430 PWM.

timer_pwm

Abgespeichert werden die Noten in einem 2D-Array. Pattern-Size ist standardmäßig auf 16 gesetzt, kann aber auch verkleinert oder vergrößert werden. Um die Patterns abzuspielen, müssen die Pattern-Positionen in einem Sequencer-Array abgespeichert werden. Die Noten in den festgelegten Patterns werden somit automatisch eingelesen und abgespielt.

Source-Code

Kompletter Source-Code: DOWNLOAD

main.c

  1. /*******************************
  2.  *          __                   
  3.  *         /\ \                  
  4.  *  __  _  \_\ \     __    ___   
  5.  * /\ \/'\ /'_' \  /'__'\ /'___\
  6.  * \/>  <//\ \ \ \/\  __//\ \__/
  7.  *  /\_/\_\ \___,_\ \____\ \____\
  8.  *  \//\/_/\/__,_ /\/____/\/____/
  9.  *
  10.  * Author:     declis
  11.  ********************************/
  12.  
  13. #include <msp430g2553.h>
  14. #include "typedef.h"
  15. #include "lib_sound.h"
  16. #include "delay.h"
  17.  
  18. void main(void)
  19. {
  20.     WDTCTL=WDTPW+WDTHOLD;
  21.     BCSCTL1=CALBC1_16MHZ;       // SMCLK=~16MHz  
  22.     DCOCTL=CALDCO_16MHZ;
  23.  
  24.     init_sound(110,ONCE);
  25.     start_sound();
  26.    
  27.     while(1);
  28. }
/*******************************
 *          __                   
 *         /\ \                  
 *  __  _  \_\ \     __    ___   
 * /\ \/'\ /'_' \  /'__'\ /'___\ 
 * \/>  <//\ \ \ \/\  __//\ \__/ 
 *  /\_/\_\ \___,_\ \____\ \____\
 *  \//\/_/\/__,_ /\/____/\/____/
 * 
 * Author:     declis
 ********************************/ 

#include <msp430g2553.h>
#include "typedef.h"
#include "lib_sound.h"
#include "delay.h"

void main(void) 
{
    WDTCTL=WDTPW+WDTHOLD; 
	BCSCTL1=CALBC1_16MHZ;		// SMCLK=~16MHz  
  	DCOCTL=CALDCO_16MHZ;

	init_sound(110,ONCE);
	start_sound();
	
	while(1);
}

lib_sound.c

  1. /*******************************
  2.  *          __                   
  3.  *         /\ \                  
  4.  *  __  _  \_\ \     __    ___   
  5.  * /\ \/'\ /'_' \  /'__'\ /'___\
  6.  * \/>  <//\ \ \ \/\  __//\ \__/
  7.  *  /\_/\_\ \___,_\ \____\ \____\
  8.  *  \//\/_/\/__,_ /\/____/\/____/
  9.  *
  10.  * Author:     declis
  11.  ********************************/
  12.  
  13. #include <msp430g2553.h>
  14. #include "typedef.h"
  15. #include "delay.h"
  16. #include "lib_sound.h"
  17.  
  18. const uint snd_pattern[][PATTERN_SIZE]={
  19. E4,E4,0,E4,0,C4,E4,0,G4,0,0,0,G3,0,0,0,                     // 00 Mario start
  20. C4,0,0,G3,0,0,E3,0,0,A3,0,B3,0,As3,A3,0,                    // 01
  21. G3,E4,0,G4,A4,0,F4,G4,0,E4,0,C4,D4,B3,0,0,                  // 02
  22. 0,0,G4,Fs4,F4,Ds4,0,E4,0,Gs3,A3,C4,0,A3,C4,D4,              // 03
  23. 0,0,G4,Fs4,F4,Ds4,0,E4,0,C5,0,C5,C5,0,0,0,                  // 04
  24. 0,0,Ds4,0,0,D4,0,0,C4,0,0,0,0,0,0,0,                        // 05
  25. C4,C4,0,C4,0,C4,D4,0,E4,C4,0,A3,G3,0,0,0,                   // 06
  26. C4,C4,0,C4,0,C4,D4,E4,0,0,0,0,0,0,0,0,                      // 07 Mario End
  27. C5,C5,A4,A4,C5,C5,D5,C5,0,E4,E4,E4,F4,F4,Fs4,Fs4,           // 08 Bob-omb start
  28. G4,G4,G4,0,G4,G3,G3,G4,G4,0,0,0,0,0,D5,Ds4,                 // 09
  29. E5,Ds4,E5,G5,G5,A5,G5,G5,C5,C5,C5,C5,0,0,G4,Gs4,            // 10
  30. A4,Gs4,A4,C5,C5,D5,C5,C5,A4,A4,A4,A4,0,0,A4,A4,             // 11
  31. G4,E4,G4,G5,0,0,G4,E4,G4,G5,0,0,G4,E4,G4,G5,                // 12
  32. 0,0,G5,G5,A5,G5,G5,D5,D5,D5,D5,D5,0,0,D4,Ds4,               // 13
  33. E4,Ds4,E4,G4,G4,A4,G4,G4,As4,As4,As4,As4,0,0,Gs4,Gs4,       // 14
  34. G4,Fs4,G4,G3,0,0,G4,Fs4,G4,G3,0,Ds4,E4,E4,E4,E4,            // 15
  35. C4,C4,C4,C4,C4,C4,C4,C4,C4,C4,C4,C4,0,0,0,0,                // 16 Bob-omb end
  36. A5,A5,A5,A5,A3,A3,A3,A3,A5,A5,A5,A5,A3,A3,A3,A3,            // 17 Princess start
  37. A5,A5,A5,A5,Fs5,Fs5,Fs5,Fs5,E5,E5,E5,E5,Ds5,Ds5,Ds5,Ds5,    // 18
  38. D5,D5,G3,G3,B3,B3,G3,G3,D5,D5,G3,G3,B3,B3,G3,G3,            // 19
  39. D5,D5,G3,G3,B3,B3,G4,B4,D5,D5,G3,G3,E5,E5,G3,G3,            // 20
  40. D5,D5,Fs3,Fs3,A3,A3,A4,A4,Fs5,Fs5,Fs3,Fs3,A5,A5,Fs3,Fs3,    // 21
  41. Gs5,Gs5,Fs3,Fs3,A5,A5,Fs3,Fs3,Gs5,A5,Gs5,Fs5,D5,D5,A4,A4,   // 22
  42. B4,B4,G3,Cs5,D5,D5,G3,E5,Fs5,F5,Fs5,G5,A5,G5,Fs5,E5,        // 23
  43. D5,Cs5,D5,A5,Cs5,C5,Cs5,A5,Cs5,C5,B4,As4,A4,Gs4,G4,E4,      // 24
  44. A4,A4,A3,A3,D4,D4,A3,A3,B4,B4,A3,A3,A4,A4,A3,A3,            // 25
  45. D4,D4,D5,D5,Cs5,Cs5,D5,D5,Fs5,Fs5,A3,A3,E5,E5,D5,D5,        // 26
  46. B4,B4,G3,G3,B4,B4,G3,G3,E5,E5,G3,G3,D5,D5,G3,G3,            // 27
  47. B4,B4,B4,B4,As4,As4,B4,B4,D5,D5,G3,G3,Cs5,Cs5,B4,B4,        // 28
  48. A4,A4,Fs3,Fs3,Fs3,Fs3,A4,A4,Fs3,Fs3,Fs3,Fs3,A5,A5,Fs3,Fs3   // 29 Princess end
  49. };
  50.  
  51. // array for playing the patterns in snd_pattern array
  52. const uchar snd_sequencer[]={
  53. 0,1,2,1,2,3,4,3,5,3,4,3,5,6,7,6,                                        // Mario
  54. 0,1,2,1,2,3,4,3,5,3,4,3,5,6,7,6,
  55. 0+8,1+8,2+8,3+8,4+8,5+8,6+8,4+8,7+8,8+8,                                // Bob-omb
  56. 0+8,1+8,2+8,3+8,4+8,5+8,6+8,4+8,7+8,8+8,
  57. 0+17,1+17,2+17,3+17,4+17,5+17,6+17,7+17,8+17,9+17,10+17,11+17,12+17,    // Princess
  58. 0+17,1+17,2+17,3+17,4+17,5+17,6+17,7+17,8+17,9+17,10+17,11+17,12+17,
  59. 0+17,1+17,2+17,3+17,4+17,5+17,6+17,7+17,8+17,9+17,10+17,11+17,12+17,
  60. 0+17
  61. };
  62.  
  63. uint snd_ms_tick;
  64. uchar snd_loop,snd_pattern_pos,snd_notes_pos,snd_sequencer_size,snd_cut_off;
  65.  
  66. void init_sound(uint bmp, uchar mode)
  67. {
  68.     P1DIR|=SPEAKER;     // output
  69.     P1SEL|=SPEAKER;     // TA0.0 option (PWM output)
  70.     P1OUT&=~SPEAKER;
  71.    
  72.     // init. ACLK (32kHz crystal)
  73.     BCSCTL3|=XCAP_3;    // CL=~12.5pF
  74.     do
  75.     {
  76.         IFG1&=~OFIFG;   // IFG1=Interrupt Flag Register 1
  77.         wait_ms(1);     // OFIFG=Oscillator Fault Interrupt Flag
  78.     }
  79.     while(IFG1&OFIFG);
  80.    
  81.     snd_cut_off=0;      // var for note dead time
  82.     snd_pattern_pos=0;  // pattern position
  83.     snd_notes_pos=0;    // notes position (current pattern)
  84.     if(mode) snd_loop=1;// play in loop
  85.     else snd_loop=0;    // play only once
  86.    
  87.     snd_sequencer_size=sizeof(snd_sequencer);   // get sequencer size
  88.     snd_ms_tick=60000/(TICKS_PER_BEAT*bmp);     // note duration
  89.    
  90.     // TimerA0 for NOTE FREQUENCY
  91.     TA0CTL|=TASSEL_2+MC_0+ID_3;         // SMCLK=16MHz, Timer Div: 8, timer in stop mode       
  92.     TA0CCR0=snd_pattern[snd_sequencer[snd_pattern_pos]][snd_notes_pos++];   // first note frequency
  93.     TA0CCR1=TA0CCR0>>DUTY_CYCLE;
  94.     TA0CCTL1|=OUTMOD_7;                 // reset/set (PWM)
  95.    
  96.     // TimerA1 for NOTE DURATION (BMP)
  97.     TA1CTL|=TASSEL_1+MC_0+ID_0;         // 32kHz crystal, no divider
  98.     //set duration (32768*snd_ms_tick*notes_duration[snd_notes_pos++])/1000-DEAD_MS;
  99.     TA1CCR0=((snd_ms_tick-DEAD_MS)<<5);
  100.     TA1CCTL0|=CCIE;
  101.     _EINT();
  102. }
  103.  
  104. void start_sound(void)
  105. {
  106.     TA0CTL|=MC_1;
  107.     TA1CTL|=MC_1;
  108. }
  109.  
  110. void stop_sound(void)
  111. {
  112.     TA0CTL&=~MC_1;
  113.     TA1CTL&=~MC_1;
  114. }
  115.  
  116. #pragma INTERRUPT (update_note);
  117. #pragma vector=TIMER1_A0_VECTOR
  118. void update_note(void)
  119. {
  120.     TA1CTL&=~MC_1;              // stop duration timer
  121.     TA1R=0;                     // set count register to zero
  122.     if(!snd_cut_off)            // DEAD TIME
  123.     {
  124.         TA0CCR0=0;              // stop note frequency timer (a ZERO will stop the timer)
  125.         TA1CCR0=DEAD_MS<<5;     // set dead time
  126.         snd_cut_off=1;          // will be used after the dead time is over (load next note)
  127.     }
  128.     else                        // NOTE TIME
  129.     {
  130.         if(snd_pattern_pos<snd_sequencer_size)     
  131.         {          
  132.             // set note            
  133.             TA0CCR0=snd_pattern[snd_sequencer[snd_pattern_pos]][snd_notes_pos++];          
  134.             TA0CCR1=TA0CCR0>>DUTY_CYCLE;
  135.            
  136.             // set duration (32768*snd_ms_tick*notes_duration[snd_notes_pos++])/1000-DEAD_MS;
  137.             //TA1CCR0=((snd_ms_tick*notes_duration[snd_notes_pos++])<<5)-DEAD_MS;
  138.             TA1CCR0=((snd_ms_tick-DEAD_MS)<<5);
  139.             if(snd_notes_pos==PATTERN_SIZE)
  140.             {  
  141.                 snd_pattern_pos++;  // next pattern
  142.                 snd_notes_pos=0;    // set note position to first note in pattern
  143.                 if(snd_pattern_pos==snd_sequencer_size&&snd_loop)   // loop mode, start again at first pattern
  144.                     snd_pattern_pos=0;
  145.             }
  146.         }
  147.         else stop_sound(); 
  148.         snd_cut_off=0;          // for dead time, see if(!snd_cut_off)
  149.     }
  150.     TA1CTL|=MC_1;               // start timer again
  151. }
/*******************************
 *          __                   
 *         /\ \                  
 *  __  _  \_\ \     __    ___   
 * /\ \/'\ /'_' \  /'__'\ /'___\ 
 * \/>  <//\ \ \ \/\  __//\ \__/ 
 *  /\_/\_\ \___,_\ \____\ \____\
 *  \//\/_/\/__,_ /\/____/\/____/
 * 
 * Author:     declis
 ********************************/ 

#include <msp430g2553.h>
#include "typedef.h"
#include "delay.h"
#include "lib_sound.h"

const uint snd_pattern[][PATTERN_SIZE]={
E4,E4,0,E4,0,C4,E4,0,G4,0,0,0,G3,0,0,0,						// 00 Mario start
C4,0,0,G3,0,0,E3,0,0,A3,0,B3,0,As3,A3,0,					// 01
G3,E4,0,G4,A4,0,F4,G4,0,E4,0,C4,D4,B3,0,0,					// 02
0,0,G4,Fs4,F4,Ds4,0,E4,0,Gs3,A3,C4,0,A3,C4,D4,				// 03
0,0,G4,Fs4,F4,Ds4,0,E4,0,C5,0,C5,C5,0,0,0,					// 04
0,0,Ds4,0,0,D4,0,0,C4,0,0,0,0,0,0,0,						// 05
C4,C4,0,C4,0,C4,D4,0,E4,C4,0,A3,G3,0,0,0,					// 06
C4,C4,0,C4,0,C4,D4,E4,0,0,0,0,0,0,0,0,						// 07 Mario End
C5,C5,A4,A4,C5,C5,D5,C5,0,E4,E4,E4,F4,F4,Fs4,Fs4,			// 08 Bob-omb start
G4,G4,G4,0,G4,G3,G3,G4,G4,0,0,0,0,0,D5,Ds4,					// 09
E5,Ds4,E5,G5,G5,A5,G5,G5,C5,C5,C5,C5,0,0,G4,Gs4,			// 10
A4,Gs4,A4,C5,C5,D5,C5,C5,A4,A4,A4,A4,0,0,A4,A4,				// 11
G4,E4,G4,G5,0,0,G4,E4,G4,G5,0,0,G4,E4,G4,G5,				// 12
0,0,G5,G5,A5,G5,G5,D5,D5,D5,D5,D5,0,0,D4,Ds4,				// 13
E4,Ds4,E4,G4,G4,A4,G4,G4,As4,As4,As4,As4,0,0,Gs4,Gs4,		// 14
G4,Fs4,G4,G3,0,0,G4,Fs4,G4,G3,0,Ds4,E4,E4,E4,E4,			// 15
C4,C4,C4,C4,C4,C4,C4,C4,C4,C4,C4,C4,0,0,0,0,				// 16 Bob-omb end
A5,A5,A5,A5,A3,A3,A3,A3,A5,A5,A5,A5,A3,A3,A3,A3,			// 17 Princess start
A5,A5,A5,A5,Fs5,Fs5,Fs5,Fs5,E5,E5,E5,E5,Ds5,Ds5,Ds5,Ds5,	// 18
D5,D5,G3,G3,B3,B3,G3,G3,D5,D5,G3,G3,B3,B3,G3,G3,			// 19
D5,D5,G3,G3,B3,B3,G4,B4,D5,D5,G3,G3,E5,E5,G3,G3,			// 20
D5,D5,Fs3,Fs3,A3,A3,A4,A4,Fs5,Fs5,Fs3,Fs3,A5,A5,Fs3,Fs3,	// 21
Gs5,Gs5,Fs3,Fs3,A5,A5,Fs3,Fs3,Gs5,A5,Gs5,Fs5,D5,D5,A4,A4,	// 22
B4,B4,G3,Cs5,D5,D5,G3,E5,Fs5,F5,Fs5,G5,A5,G5,Fs5,E5,		// 23
D5,Cs5,D5,A5,Cs5,C5,Cs5,A5,Cs5,C5,B4,As4,A4,Gs4,G4,E4,		// 24
A4,A4,A3,A3,D4,D4,A3,A3,B4,B4,A3,A3,A4,A4,A3,A3,			// 25
D4,D4,D5,D5,Cs5,Cs5,D5,D5,Fs5,Fs5,A3,A3,E5,E5,D5,D5,		// 26
B4,B4,G3,G3,B4,B4,G3,G3,E5,E5,G3,G3,D5,D5,G3,G3,			// 27
B4,B4,B4,B4,As4,As4,B4,B4,D5,D5,G3,G3,Cs5,Cs5,B4,B4,		// 28
A4,A4,Fs3,Fs3,Fs3,Fs3,A4,A4,Fs3,Fs3,Fs3,Fs3,A5,A5,Fs3,Fs3	// 29 Princess end
};

// array for playing the patterns in snd_pattern array
const uchar snd_sequencer[]={
0,1,2,1,2,3,4,3,5,3,4,3,5,6,7,6, 										// Mario
0,1,2,1,2,3,4,3,5,3,4,3,5,6,7,6, 
0+8,1+8,2+8,3+8,4+8,5+8,6+8,4+8,7+8,8+8,								// Bob-omb
0+8,1+8,2+8,3+8,4+8,5+8,6+8,4+8,7+8,8+8,
0+17,1+17,2+17,3+17,4+17,5+17,6+17,7+17,8+17,9+17,10+17,11+17,12+17,	// Princess
0+17,1+17,2+17,3+17,4+17,5+17,6+17,7+17,8+17,9+17,10+17,11+17,12+17,
0+17,1+17,2+17,3+17,4+17,5+17,6+17,7+17,8+17,9+17,10+17,11+17,12+17,
0+17
};

uint snd_ms_tick;
uchar snd_loop,snd_pattern_pos,snd_notes_pos,snd_sequencer_size,snd_cut_off;

void init_sound(uint bmp, uchar mode)
{
	P1DIR|=SPEAKER;		// output
	P1SEL|=SPEAKER;		// TA0.0 option (PWM output)
	P1OUT&=~SPEAKER;
	
	// init. ACLK (32kHz crystal)
	BCSCTL3|=XCAP_3;	// CL=~12.5pF
	do
	{
		IFG1&=~OFIFG;	// IFG1=Interrupt Flag Register 1
		wait_ms(1);		// OFIFG=Oscillator Fault Interrupt Flag
	}
	while(IFG1&OFIFG);
	
	snd_cut_off=0;		// var for note dead time
	snd_pattern_pos=0;	// pattern position
	snd_notes_pos=0;	// notes position (current pattern)
	if(mode) snd_loop=1;// play in loop
	else snd_loop=0;	// play only once
	
	snd_sequencer_size=sizeof(snd_sequencer);	// get sequencer size
	snd_ms_tick=60000/(TICKS_PER_BEAT*bmp);		// note duration
	
	// TimerA0 for NOTE FREQUENCY
    TA0CTL|=TASSEL_2+MC_0+ID_3;			// SMCLK=16MHz, Timer Div: 8, timer in stop mode		
    TA0CCR0=snd_pattern[snd_sequencer[snd_pattern_pos]][snd_notes_pos++];	// first note frequency
    TA0CCR1=TA0CCR0>>DUTY_CYCLE;
    TA0CCTL1|=OUTMOD_7;					// reset/set (PWM)
    
    // TimerA1 for NOTE DURATION (BMP)
    TA1CTL|=TASSEL_1+MC_0+ID_0;			// 32kHz crystal, no divider
    //set duration (32768*snd_ms_tick*notes_duration[snd_notes_pos++])/1000-DEAD_MS;
    TA1CCR0=((snd_ms_tick-DEAD_MS)<<5);
    TA1CCTL0|=CCIE;
    _EINT();
}

void start_sound(void)
{
    TA0CTL|=MC_1;
    TA1CTL|=MC_1;
}

void stop_sound(void)
{
    TA0CTL&=~MC_1;
    TA1CTL&=~MC_1;
}

#pragma INTERRUPT (update_note);
#pragma vector=TIMER1_A0_VECTOR
void update_note(void) 
{
	TA1CTL&=~MC_1;				// stop duration timer
	TA1R=0;						// set count register to zero
	if(!snd_cut_off)			// DEAD TIME
	{
		TA0CCR0=0;				// stop note frequency timer (a ZERO will stop the timer)
		TA1CCR0=DEAD_MS<<5;		// set dead time
		snd_cut_off=1;			// will be used after the dead time is over (load next note)
	}
	else 						// NOTE TIME
	{
		if(snd_pattern_pos<snd_sequencer_size) 		
		{ 			
			// set note 			
			TA0CCR0=snd_pattern[snd_sequencer[snd_pattern_pos]][snd_notes_pos++]; 			
			TA0CCR1=TA0CCR0>>DUTY_CYCLE;
			
			// set duration (32768*snd_ms_tick*notes_duration[snd_notes_pos++])/1000-DEAD_MS;
			//TA1CCR0=((snd_ms_tick*notes_duration[snd_notes_pos++])<<5)-DEAD_MS;
			TA1CCR0=((snd_ms_tick-DEAD_MS)<<5);
			if(snd_notes_pos==PATTERN_SIZE) 
			{	
				snd_pattern_pos++;	// next pattern
				snd_notes_pos=0;	// set note position to first note in pattern
				if(snd_pattern_pos==snd_sequencer_size&&snd_loop)	// loop mode, start again at first pattern
					snd_pattern_pos=0;
			}
		}
		else stop_sound();	
		snd_cut_off=0;			// for dead time, see if(!snd_cut_off)
	}
	TA1CTL|=MC_1;				// start timer again
}

lib_sound.h

  1. /*******************************
  2.  *          __                   
  3.  *         /\ \                  
  4.  *  __  _  \_\ \     __    ___   
  5.  * /\ \/'\ /'_' \  /'__'\ /'___\
  6.  * \/>  <//\ \ \ \/\  __//\ \__/
  7.  *  /\_/\_\ \___,_\ \____\ \____\
  8.  *  \//\/_/\/__,_ /\/____/\/____/
  9.  *
  10.  * Author:     declis
  11.  ********************************/
  12.  
  13. #ifndef LIB_SOUND_H_
  14. #define LIB_SOUND_H_
  15.  
  16. #define SPEAKER         0x0004  // P1.2
  17. #define LOOP            1       // play in loop
  18. #define ONCE            0       // play once
  19. #define PATTERN_SIZE    16      // size of pattern
  20. #define DEAD_MS         10      // dead time of note (cut off)
  21. #define DUTY_CYCLE      3       // 2^DUTY_CYLCE ; 2^1=2 (50%); 2^2=4 (25%); 2^3=8 (12.5%)
  22. #define TICKS_PER_BEAT  4       // 4/4
  23.  
  24. void init_sound(uint,uchar);
  25. void start_sound(void);
  26. void stop_sound(void);
  27.  
  28. // Precalulated notes: CPUCLK/(note_freq*TimerDiv)
  29. // 16MHz/(note_freq*8)
  30. #define C8  477
  31. #define B7  506
  32. #define As7 536
  33. #define A7  568
  34. #define Gs7 601
  35. #define G7  637
  36. #define Fs7 675
  37. #define F7  715
  38. #define E7  758
  39. #define Ds7 803
  40. #define D7  851
  41. #define Cs7 901
  42. #define C7  955
  43. #define B6  1012
  44. #define As6 1072
  45. #define A6  1136
  46. #define Gs6 1203
  47. #define G6  1275
  48. #define Fs6 1351
  49. #define F6  1431
  50. #define E6  1516
  51. #define Ds6 1607
  52. #define D6  1702
  53. #define Cs6 1803
  54. #define C6  1911
  55. #define B5  2024
  56. #define As5 2145
  57. #define A5  2272
  58. #define Gs5 2407
  59. #define G5  2551
  60. #define Fs5 2702
  61. #define F5  2863
  62. #define E5  3033
  63. #define Ds5 3214
  64. #define D5  3405
  65. #define Cs5 3607
  66. #define C5  3822
  67. #define B4  4049
  68. #define As4 4290
  69. #define A4  4545
  70. #define Gs4 4815
  71. #define G4  5102
  72. #define Fs4 5405
  73. #define F4  5726
  74. #define E4  6067
  75. #define Ds4 6428
  76. #define D4  6810
  77. #define Cs4 7215
  78. #define C4  7644
  79. #define B3  8099
  80. #define As3 8580
  81. #define A3  9090
  82. #define Gs3 9631
  83. #define G3  10204
  84. #define Fs3 10810
  85. #define F3  11453
  86. #define E3  12134
  87. #define Ds3 12856
  88. #define D3  13621
  89. #define Cs3 14430
  90. #define C3  15289
  91. #define B2  16198
  92. #define As2 17161
  93. #define A2  18181
  94. #define Gs2 19262
  95. #define G2  20408
  96. #define Fs2 21621
  97. #define F2  22907
  98. #define E2  24269
  99. #define Ds2 25712
  100. #define D2  27241
  101. #define Cs2 28861
  102. #define C2  30578
  103. #define B1  32396
  104. #define As1 34322
  105. #define A1  36363
  106. #define Gs1 38525
  107. #define G1  40816
  108. #define Fs1 43243
  109. #define F1  45815
  110. #define E1  48539
  111. #define Ds1 51425
  112. #define D1  54483
  113. #define Cs1 57723
  114. #define C1  61156
  115. #define B0  64792
  116.  
  117. #endif /*LIB_SOUND_H_*/
/*******************************
 *          __                   
 *         /\ \                  
 *  __  _  \_\ \     __    ___   
 * /\ \/'\ /'_' \  /'__'\ /'___\ 
 * \/>  <//\ \ \ \/\  __//\ \__/ 
 *  /\_/\_\ \___,_\ \____\ \____\
 *  \//\/_/\/__,_ /\/____/\/____/
 * 
 * Author:     declis
 ********************************/ 

#ifndef LIB_SOUND_H_
#define LIB_SOUND_H_

#define SPEAKER			0x0004	// P1.2
#define LOOP			1		// play in loop
#define ONCE			0		// play once
#define PATTERN_SIZE	16		// size of pattern
#define DEAD_MS 		10		// dead time of note (cut off)
#define DUTY_CYCLE		3		// 2^DUTY_CYLCE ; 2^1=2 (50%); 2^2=4 (25%); 2^3=8 (12.5%)
#define TICKS_PER_BEAT 	4		// 4/4

void init_sound(uint,uchar);
void start_sound(void);
void stop_sound(void);

// Precalulated notes: CPUCLK/(note_freq*TimerDiv)
// 16MHz/(note_freq*8)
#define	C8	477
#define	B7	506
#define	As7	536
#define	A7	568
#define	Gs7	601
#define	G7	637
#define	Fs7	675
#define	F7	715
#define	E7	758
#define	Ds7	803
#define	D7	851
#define	Cs7	901
#define	C7	955
#define	B6	1012
#define	As6	1072
#define	A6	1136
#define	Gs6	1203
#define	G6	1275
#define	Fs6	1351
#define	F6	1431
#define	E6	1516
#define	Ds6	1607
#define	D6	1702
#define	Cs6	1803
#define	C6	1911
#define	B5	2024
#define	As5	2145
#define	A5	2272
#define	Gs5	2407
#define	G5	2551
#define	Fs5	2702
#define	F5	2863
#define	E5	3033
#define	Ds5	3214
#define	D5	3405
#define	Cs5	3607
#define	C5	3822
#define	B4	4049
#define	As4	4290
#define	A4	4545
#define	Gs4	4815
#define	G4	5102
#define	Fs4	5405
#define	F4	5726
#define	E4	6067
#define	Ds4	6428
#define	D4	6810
#define	Cs4	7215
#define	C4	7644
#define	B3	8099
#define	As3	8580
#define	A3	9090
#define	Gs3	9631
#define	G3	10204
#define	Fs3	10810
#define	F3	11453
#define	E3	12134
#define	Ds3	12856
#define	D3	13621
#define	Cs3	14430
#define	C3	15289
#define	B2	16198
#define	As2	17161
#define	A2	18181
#define	Gs2	19262
#define	G2	20408
#define	Fs2	21621
#define	F2	22907
#define	E2	24269
#define	Ds2	25712
#define	D2	27241
#define	Cs2	28861
#define	C2	30578
#define	B1	32396
#define	As1	34322
#define	A1	36363
#define	Gs1	38525
#define	G1	40816
#define	Fs1	43243
#define	F1	45815
#define	E1	48539
#define	Ds1	51425
#define	D1	54483
#define	Cs1	57723
#define	C1	61156
#define	B0	64792

#endif /*LIB_SOUND_H_*/

 

Für dieses Beispiel habe ich einfach ein paar Melodien von einem anderen Sound-Programm benutzt: bobsomers/msp430-launchpad-music

8 thoughts on “MSP430 Sound und Musik erzeugen (Tracker)

  1. Benjo

    Hallo,

    vielen Dank dass du dein Projekt hier reingestellt hast.

    Ich hab nun versucht, dein Code mal auf meinen Controller zu testen, bekomme leider die folgende Fehlermeldung:

    Error[e46]: Undefined external “__delay_cycles” referred in delay (
    C:\Users\…\Desktop\…\Debug\Obj\delay.r43 )

    Hoffe du weißt da weiter.

    Reply
  2. declis Post author

    Der Compiler kennt die Funktion __delay_cycles(value) in dem Modul delay.c nicht. Wieso auch immer. Welche Code Composer Version benutzt du? Sind die Projekteinstellungen auf den 2553 angepasst?

    Reply
  3. Benjo

    Vielen Dank für die Antwort.

    Ja unter den Projekteinstellungen habe ich den 2553 ausgewählt.

    Ne als IDE benutze ich den IAR.

    Reply
  4. declis Post author

    Okay, IAR habe ich in Verbindung mit dem MSP430 noch nie benutzt (nur mit einem 8051). Deswegen kann ich nur raten. Könntest du in der Datei main.c und lib_sound.c die Zeile #include msp430g2553.h durch #include io430.h ersetzen und nochmal kompilieren?

    Reply
  5. Benjo

    Hat leider auch nicht funktioniert. Bekomme sogar noch eine weitere Fehlermeldung dazu:

    Error[e46]: Undefined external “__delay_cycles” referred in delay
    (C:\Users\…\Desktop\…\Debug\Obj\delay.r43)

    Error[e46]: Undefined external “_EINT” referred in lib_sound
    (C:\Users\…\Desktop\…\Debug\Obj\lib_sound.r43 )

    Reply
  6. declis Post author

    Okay, dann wieder msp430g2553.h einbinden und mal in delay.c (ganz oben) die Datei intrinsics.h einbinden. Dort ist __delay_cycles jedenfalls vorhanden (wenn ich das gerade richtig sehe).

    Reply
  7. Benjo

    Ja super hat funktioniert. Vielen Dank.

    Aber mir stellt sich die Frage, obwohl wir beide den gleichen Prozessor benutzen, wieso hat es bei dir ohne “intrinsics.h” funktioniert und bei mir ist es notwendig?

    Kann es wirklich nur bei der IDE liegen?

    Reply
  8. declis Post author

    Perfekt! Liegt dann wohl an IAR, da muss man dann die Header-Datei mit einbinden. Bei CCS wird es wohl automatisch eingebunden.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload CAPTCHA.