ZHCAG09 November 2025 MSPM0G3507
#include "ti_msp_dl_config.h"
/* musical notes frequencies in Hz - 6th octave */
#define NOTE_C 1047 // C6
#define NOTE_D 1175 // D6
#define NOTE_E 1319 // E6
#define NOTE_F 1397 // F6
#define NOTE_G 1568 // G6
#define NOTE_A 1760 // A6
#define NOTE_B 1976 // B6
/* sine wave lookup table for DAC output */
#define SINE_POINTS 64
const uint16_t gSineTable[] = {2048, 2248, 2447, 2642, 2831, 3013,
3185, 3347, 3496, 3631, 3750, 3854, 3940, 4007, 4056, 4086, 4095, 4086,
4056, 4007, 3940, 3854, 3750, 3631, 3496, 3347, 3185, 3013, 2831, 2642,
2447, 2248, 2048, 1847, 1648, 1453, 1264, 1082, 910, 748, 599, 464, 345,
241, 155, 88, 39, 9, 0, 9, 39, 88, 155, 241, 345, 464, 599, 748, 910, 1082,
1264, 1453, 1648, 1847};
/* Variables for tone generation */
#define PHASE_FRAC_BITS 24
uint32_t gPhaseAccumulator = 0;
uint32_t gPhaseIncrement = 0;
/* 16.39kHz sample rate (61us timer period). May require some shifts depending on the piezo used */
uint32_t gSampleRate = 16393;
/* Melody sequence: "BAG BAG GGGGAAAA BAG", "Hot crossed buns" */
/* 0u represents a rest, or a tone of "0" */
static const uint32_t gMelodyFreqs[] = {
NOTE_B, 0u, NOTE_A, 0u, NOTE_G, 0u, 0u,
NOTE_B, 0u, NOTE_A, 0u, NOTE_G, 0u, 0u,
NOTE_G, 0u, NOTE_G, 0u, NOTE_G, 0u, NOTE_G, 0u,
NOTE_A, 0u, NOTE_A, 0u, NOTE_A, 0u, NOTE_A, 0u,
NOTE_B, 0u, NOTE_A, 0u, NOTE_G, 0u
};
/* durations in milliseconds for each entry above:
full note -> 500ms, half note -> 250ms, short rest -> 80ms, phrase separator rest -> 250ms */
static const uint16_t gMelodyDurMs[] = {
/* One phrase per line */
500, 80, 500, 80, 500, 80, 250,
500, 80, 500, 80, 500, 80, 250,
250, 80, 250, 80, 250, 80, 250, 80,
250, 80, 250, 80, 250, 80, 250, 80,
500, 80, 500, 80, 500, 2000
};
/* defines for melody state machine*/
#define MELODY_LENGTH (sizeof(gMelodyFreqs)/sizeof(gMelodyFreqs[0]))
static uint16_t gCurrentMelodyIndex = 0;
static uint32_t gNoteSamplesRemaining = 0;
/* set the phase increment for a given frequency */
void setToneFrequency(uint32_t frequency) {
if (frequency == 0) {
gPhaseIncrement = 0;
return;
}
/* compute increment with 24 fractional bits: inc = freq * SINE_POINTS * 2^PHASE_FRAC_BITS / sampleRate */
/* allows for smooth frequency shifting */
uint64_t inc = (uint64_t)frequency * (uint64_t)SINE_POINTS * ((uint64_t)1 << PHASE_FRAC_BITS);
inc /= gSampleRate;
gPhaseIncrement = (uint32_t)inc;
}
/* play each note from the melody based on index (updated by isr) */
static void startMelodyNote(uint16_t idx)
{
if (idx >= MELODY_LENGTH) {
idx = 0;
}
gCurrentMelodyIndex = idx;
uint32_t freq = gMelodyFreqs[idx];
setToneFrequency(freq);
/* compute note duration */
uint32_t durMs = gMelodyDurMs[idx];
gNoteSamplesRemaining = (durMs * gSampleRate) / 1000u;
/* if duration is zero, click note for one sample*/
if (gNoteSamplesRemaining == 0) {
gNoteSamplesRemaining = 1;
}
}
int main(void)
{
SYSCFG_DL_init();
/* Start the first note of the melody */
startMelodyNote(0);
/* Start timer to trigger DAC */
DL_TimerG_startCounter(TIMER_0_INST);
/* Enable DAC interrupt to update output */
NVIC_EnableIRQ(DAC12_INT_IRQN);
/* Calling WFI after calling DL_SYSCTL_enableSleepOnExit will result in
* only ISR code to be executed. This is done to showcase the device's
* low power consumption when sleeping.
*/
DL_SYSCTL_enableSleepOnExit();
while (1) {
__WFI();
}
}
void DAC12_IRQHandler(void)
{
switch (DL_DAC12_getPendingInterrupt(DAC0)) {
case DL_DAC12_IIDX_FIFO_1_2_EMPTY:
{
/* code for rest */
if (gPhaseIncrement == 0) {
DL_DAC12_output12(DAC0, 2048);
} else { /*code for tone, walk through phase table */
uint32_t tableIndex = (gPhaseAccumulator >> PHASE_FRAC_BITS) & (SINE_POINTS - 1);
DL_DAC12_output12(DAC0, gSineTable[tableIndex]);
gPhaseAccumulator += gPhaseIncrement;
}
/* state machine forward */
if (gNoteSamplesRemaining > 0) {
gNoteSamplesRemaining--;
}
if (gNoteSamplesRemaining == 0) {
/* move to next note, wrap around */
uint16_t next = gCurrentMelodyIndex + 1;
/* causes song to loop*/
if (next >= MELODY_LENGTH) {
next = 0;
}
startMelodyNote(next);
}
}
break;
/* unused */
case DL_DAC12_IIDX_FIFO_3_4_EMPTY:
case DL_DAC12_IIDX_NO_INT:
case DL_DAC12_IIDX_MODULE_READY:
case DL_DAC12_IIDX_FIFO_FULL:
case DL_DAC12_IIDX_FIFO_1_4_EMPTY:
case DL_DAC12_IIDX_FIFO_EMPTY:
case DL_DAC12_IIDX_FIFO_UNDERRUN:
case DL_DAC12_IIDX_DMA_DONE:
break;
}
}