单片机实现《生日快乐歌》
实现一个“生日快乐歌”是单片机应用中的一个经典项目,通过它可以学习如何控制声音输出、使用定时器、以及如何处理按键输入等功能。本项目将利用单片机生成《生日快乐歌》的音频信号,并通过蜂鸣器播放出来。
项目目标
本项目的目标是设计并实现一个基于8051单片机的“生日快乐歌”播放系统。系统会通过蜂鸣器输出《生日快乐歌》的旋律,当按下一个按键时,系统播放这首歌。
一、硬件设计
1.1 单片机选择
本项目采用8051单片机,它具备丰富的I/O端口,可以与蜂鸣器连接,并且支持定时器,可以用来产生音频信号。
1.2 外部硬件
- 蜂鸣器:作为输出设备,通过蜂鸣器发出不同频率的音符,模拟乐曲的旋律。
- 按键:用于触发《生日快乐歌》的播放。假设使用一个简单的按键,按下时开始播放歌曲。
- 定时器:用定时器来产生一定频率的脉冲信号,以控制蜂鸣器发出不同的音高。
- 电源和电阻:用于电路的供电和保护。
1.3 硬件连接
- 蜂鸣器连接:蜂鸣器连接到P1.0端口,利用PWM信号驱动蜂鸣器发出声音。
- 按键连接:按键连接到P3.0端口,用来触发播放《生日快乐歌》。
二、系统设计
2.1 功能模块
- 音符生成模块:通过定时器产生不同频率的脉冲信号,驱动蜂鸣器发出不同频率的声音,形成音符。
- 按键触发模块:通过按键输入,触发歌曲的播放。
- 歌曲存储和播放模块:存储《生日快乐歌》中的音符频率和时长,依次播放。
2.2 数据结构
- 音符频率:将歌曲中的每个音符频率(如C4、D4等)存储为一个数组,方便播放。
- 音符时长:每个音符的时长也可以通过数组存储,控制每个音符的持续时间。
- 当前音符:当前播放的音符。
2.3 歌曲结构
《生日快乐歌》的音符和时长如下(以C4为基础音符):
音符 | 频率(Hz) | 时长(ms) |
---|---|---|
C4 | 261.63 | 400 |
C4 | 261.63 | 400 |
D4 | 293.66 | 400 |
C4 | 261.63 | 800 |
F4 | 349.23 | 400 |
F4 | 349.23 | 400 |
E4 | 329.63 | 400 |
C4 | 261.63 | 800 |
C4 | 261.63 | 400 |
C4 | 261.63 | 400 |
D4 | 293.66 | 400 |
C4 | 261.63 | 800 |
三、程序设计
3.1 定时器中断
利用定时器中断来生成频率对应的音符。通过设置定时器初值,可以生成一定频率的脉冲信号,从而控制蜂鸣器发出相应的音符。
3.2 主程序框架
- 初始化定时器和蜂鸣器。
- 按键扫描:监听按键的输入,当按下按键时触发歌曲播放。
- 音符生成和播放:根据音符的频率和时长,通过定时器产生相应的脉冲信号,控制蜂鸣器的鸣响。
- 音符播放结束后继续播放下一音符,直到歌曲播放完毕。
3.3 代码实现
#include <reg51.h> // 包含8051的寄存器定义// 定义蜂鸣器连接端口
#define BEEP P1_0// 定义按键连接端口
#define BUTTON P3_0// 定义音符的频率(单位:Hz)
#define C4 261
#define D4 293
#define E4 329
#define F4 349// 定义音符时长(单位:毫秒)
#define NOTE_DURATION 400 // 默认音符时长// 定义音符数组,按照《生日快乐歌》的音符顺序
unsigned int melody[] = {C4, C4, D4, C4, F4, F4, E4, C4, C4, C4, D4, C4};
unsigned int melody_duration[] = {NOTE_DURATION, NOTE_DURATION, NOTE_DURATION, NOTE_DURATION, NOTE_DURATION, NOTE_DURATION, NOTE_DURATION, NOTE_DURATION, NOTE_DURATION, NOTE_DURATION, NOTE_DURATION, NOTE_DURATION};// 定时器初始化,产生固定频率的脉冲
void timer0_init() {TMOD = 0x01; // 设置定时器0为模式1(16位定时器)IE = 0x82; // 使能定时器中断TR0 = 1; // 启动定时器0
}// 延时函数(单位:毫秒)
void delay_ms(unsigned int ms) {unsigned int i, j;for (i = 0; i < ms; i++) {for (j = 0; j < 120; j++) {// 空循环,产生延时}}
}// 播放音符
void play_tone(unsigned int frequency, unsigned int duration) {unsigned int i;unsigned int delay_time = 1000000 / frequency; // 计算每个周期的延时unsigned int cycles = frequency * duration / 1000; // 计算音符持续的周期数for (i = 0; i < cycles; i++) {BEEP = 1; // 启动蜂鸣器delay_ms(delay_time / 2); // 延时一半周期BEEP = 0; // 关闭蜂鸣器delay_ms(delay_time / 2); // 延时另一半周期}
}// 按键扫描函数
unsigned char key_scan() {if (BUTTON == 0) {delay_ms(20); // 去抖动return 1; // 按键被按下}return 0;
}// 主程序
void main() {unsigned int i;timer0_init(); // 初始化定时器while (1) {if (key_scan()) { // 如果按键被按下for (i = 0; i < sizeof(melody) / sizeof(melody[0]); i++) {play_tone(melody[i], melody_duration[i]); // 播放音符delay_ms(100); // 音符之间的间隔}}}
}
四、程序说明
- 定时器初始化:
timer0_init()
函数初始化定时器0为模式1(16位定时器),启用定时器中断来产生音频信号。 - 按键扫描:
key_scan()
函数用来检测按键是否被按下,若按键按下,则启动播放《生日快乐歌》。 - 音符播放:
play_tone()
函数根据音符的频率和时长,生成脉冲信号,控制蜂鸣器发出相应的音符。 - 音符和时长数组:
melody[]
数组存储《生日快乐歌》的音符,melody_duration[]
数组存储每个音符的时长。
五、优化与扩展
- 多音符支持:可以扩展音符数组,增加更多的音符和歌曲。
- 使用LCD显示:通过LCD显示屏显示歌曲的进度或歌曲名。
- 音量调节:通过PWM信号控制蜂鸣器的音量,提供音量调节功能。
- 多段歌曲支持:将不同的歌曲分段存储,增加多种歌曲选择功能。
六、总结
通过本项目,我们学习了如何通过单片机控制蜂鸣器发出不同频率的声音,模拟乐曲的旋律。同时,这个项目也展示了如何用8051单片机处理定时器、按键输入和音符生成等基本功能。