Designing a Music Box Based on 51 Microcontroller
Today we will design an interesting music box project that combines the LCD1602 display and a buzzer to allow our microcontroller to play music.
System Design Block Diagram
Note Frequency Table
// Note frequency definitions (unit: Hz)
unsigned int TONE_TAB[] = {
523, //do-low
587, //re
659, //mi
698, //fa
784, //so
880, //la
988, //si
1047 //do-high
};
// Note duration definitions
unsigned char BEAT_TAB[] = {
1, // whole note
2, // half note
4, // quarter note
8 // eighth note
};
Basic Hardware Driver
1. Buzzer Control Function
// Buzzer pin definition
sbit BEEP = P2^3;
// Note sound function
void Play_Tone(unsigned int freq, unsigned char time)
{
unsigned int i, cycles;
unsigned int period;
period = 1000000/freq; // Calculate period (us)
cycles = freq * time/1000; // Calculate loop count
for(i=0; i<cycles; i++)
{
BEEP = 1;
delay_us(period/2);
BEEP = 0;
delay_us(period/2);
}
}
2. LCD Display Control
// Display current playing information
void Display_Music_Info(unsigned char *music_name,
unsigned char note_index)
{
LCD_WriteCmd(0x80);
LCD_WriteString(music_name);
LCD_WriteCmd(0xC0);
LCD_WriteString("Playing...");
LCD_WriteData(note_index + '0');
}
Music Data Structure
1. Score Definition
// Music structure definition
typedef struct {
unsigned char tone; // Note
unsigned char beat; // Beat
} MUSIC_NOTE;
// Little Star score
MUSIC_NOTE LITTLE_STAR[] = {
{1,4}, {1,4}, {5,4}, {5,4}, // do do so so
{6,4}, {6,4}, {5,2}, // la la so
{4,4}, {4,4}, {3,4}, {3,4}, // fa fa mi mi
{2,4}, {2,4}, {1,2}, // re re do
{0,1} // End symbol
};
2. Music Playback Control
void Play_Music(MUSIC_NOTE *music)
{
unsigned char i = 0;
while(music[i].tone != 0) // Not end symbol
{
if(KEY_STOP == 0) // Check stop key
break;
// Play note
Play_Tone(TONE_TAB[music[i].tone-1],
1000/music[i].beat);
// Display progress
Display_Music_Info("Little Star", i);
// Short pause between notes
delay_ms(50);
i++;
}
}
Function Expansion Implementation
1. Music Selection Menu
void Music_Select()
{
unsigned char music_index = 0;
char *music_list[] = {
"Little Star",
"Happy Birthday",
"Jingle Bells"
};
LCD_WriteCmd(0x80);
LCD_WriteString("Select Music:");
while(1)
{
LCD_WriteCmd(0xC0);
LCD_WriteString(music_list[music_index]);
if(KEY_UP == 0) // Scroll up
{
delay_ms(10);
if(KEY_UP == 0)
music_index++;
}
if(KEY_DOWN == 0) // Scroll down
{
delay_ms(10);
if(KEY_DOWN == 0)
music_index--;
}
if(KEY_OK == 0) // Confirm
return music_index;
}
}
2. Volume Control
void Volume_Control(unsigned char *volume)
{
if(KEY_VOL_UP == 0)
{
delay_ms(10);
if(KEY_VOL_UP == 0 && *volume < 8)
(*volume)++;
}
if(KEY_VOL_DOWN == 0)
{
delay_ms(10);
if(KEY_VOL_DOWN == 0 && *volume > 0)
(*volume)--;
}
// Display volume bar
LCD_WriteCmd(0xC0);
LCD_WriteString("VOL:");
for(unsigned char i=0; i<*volume; i++)
LCD_WriteData('*');
}
3. Tempo Adjustment
void Tempo_Adjust(unsigned char *tempo)
{
unsigned int base_time;
base_time = 1000 / (*tempo); // Base beat time value
if(KEY_TEMPO_UP == 0)
(*tempo)++;
if(KEY_TEMPO_DOWN == 0)
(*tempo)--;
LCD_WriteCmd(0xC0);
LCD_WriteString("Tempo:");
LCD_WriteNum(*tempo);
}
Precautions
- Buzzer Driver Optimization
// Use timer to generate square wave
void Timer0_PWM()
{
TH0 = 0xFF; // Timer reload value
TL0 = 0xF0;
TR0 = 1; // Start timer
}
- Key Debounce Processing
bit Key_Scan(unsigned char key_pin)
{
if(key_pin == 0)
{
delay_ms(10); // Debounce delay
if(key_pin == 0)
{
while(!key_pin); // Wait for release
return 1;
}
}
return 0;
}
- Power Saving Mode
void Power_Save()
{
if(no_operation_time > 30) // 30 seconds without operation
{
LCD_WriteCmd(0x08); // Turn off display
PCON |= 0x02; // Enter power-down mode
}
}
Practical Suggestions:
- Set volume reasonably
- Add startup animation
- Save user settings
- Support resume playback
The music box, although simple in principle, requires attention to detail to make it fun. It is recommended to add more user-friendly interaction designs after implementing the basic functions.