Search code examples
real-timefrequencyinterrupt-handlingmsp430launchpad

MSP430 Music Player Can't Produce Note Higher than Certain Frequency


I'm trying to complete an assignment that requires me to make a music player using the MSP430 microprocessor and Launchpad kit. I have the player completely working, but for some reason when I try to play above a certain note, it outputs rapid clicking instead of the tone.

I know the speaker can produce a higher tone, so I am fairly certain it's an issue with my software, probably creating some sort of math error. Here is my code (at least the part that handles the notes):

asm(" .length 10000");
asm(" .width 132");

#include "msp430g2553.h"
//-----------------------
// define the bit mask (within P1) corresponding to output TA0
#define TA0_BIT 0x02
// define the port and location for the button (this is the built in button)
// specific bit for the button
#define BUTTON_BIT 0x04

#define PLUS_BUTTON 0x08  //Defines the "GO FASTER" button to P1.3
#define MINUS_BUTTON 0x10 //Defines the "SLOW DOWN" button to P1.4
#define SHIFT 0x20
//----------------------------------
// Some global variables (mainly to look at in the debugger)
volatile unsigned halfPeriod; // half period count for the timer
volatile unsigned long intcount=0; // number of times the interrupt has occurred
volatile unsigned soundOn=0; // state of sound: 0 or OUTMOD_4 (0x0080)
volatile int noteCount = 0;
volatile int noteLength = 0;
volatile int deltaHP=1; // step in half period per half period
volatile unsigned int plus_on;
volatile unsigned int minus_on;
volatile double speed = 1;
volatile int shiftkey = 0;

static const int noteArray[] =   {800, 1000, 900, 800}; //THESE ARE THE NOTES
static const int noteLengths[] = {200,  500,  500, 500}; 

void init_timer(void); // routine to setup the timer
void init_button(void); // routine to setup the button
// ++++++++++++++++++++++++++
void main(){
    WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
    BCSCTL1 = CALBC1_1MHZ; // 1Mhz calibration for clock
    DCOCTL = CALDCO_1MHZ;
    //halfPeriod=noteArray[0]; // initial half-period at lowest frequency
    init_timer(); // initialize timer
    init_button(); // initialize the button
    _bis_SR_register(GIE+LPM0_bits);// enable general interrupts and power down CPU
}
// +++++++++++++++++++++++++++
// Sound Production System
void init_timer(){ // initialization and start of timer
    TA0CTL |=TACLR; // reset clock
    TA0CTL =TASSEL1+ID_0+MC_2; // clock source = SMCLK, clock divider=1, continuous mode,
    TA0CCTL0=soundOn+CCIE; // compare mode, outmod=sound, interrupt CCR1 on
    TA0CCR0 = TAR+noteArray[0]; // time for first alarm
    P1SEL|=TA0_BIT; // connect timer output to pin
    P1DIR|=TA0_BIT;
}
// +++++++++++++++++++++++++++
void interrupt sound_handler(){
    TACCR0 += (noteArray[noteCount]); // advance 'alarm' time
    if (soundOn){ // change half period if the sound is playing
        noteLength++;
        if (noteLength >= (speed* noteLengths[noteCount])) {
            noteLength=0;
            noteCount++;
            if (noteCount == sizeof(noteArray)/sizeof(int)) {
                //halfPeriod += deltaHP;
                noteCount = 0;
                //deltaHP=-deltaHP;
            }
        }
    }
    TA0CCTL0 = CCIE + soundOn; //  update control register with current soundOn
    ++intcount; // advance debug counter
}
ISR_VECTOR(sound_handler,".int09") // declare interrupt vector

Currently I have just 4 random notes in there with 4 random lengths to demonstrate the error. The strange clicking noise happens somewhere between a note value of 800 and 900. Am I just missing something in my code that would produce an error for a number smaller than 8xx? I don't see any spots for division errors or the like but I could be wrong. Thank you.

ALSO: I should note that when the error occurs, the clicking lasts a very long time, much longer than the corresponding length for that note, but it isn't permanent. Eventually the player moves on to the next note and plays it normally as long as it's larger than 900 or so.


Solution

  • If the interrupt handler does not execute fast enough, the setting of the next event (TACCR0 += noteArray[...]) will come too late, i.e., after that timer value has already been reached. So the next timer interrupt will fire not after 800 ticks but after 216+800 ticks.

    You might try to optimize the interrup handler function. In particular, floating-point emulation can take hundreds of cycles; remove speed.

    However, instead of toggling the output in software, you should take advantage of the hardware capabilites, and generate the waveform with the PWM function: run the timer in Up mode, and use set/reset output mode for the second CCR (see section 12.2.5.2 of the User's Guide). (This implies that you need timer interrupts only to start/stop notes, so to fit into the 216 limit, you probably want to use a second timer based on a much slower clock.)