51单片机数字音乐盒实战:定时器发声原理、电路与C51程序
2026/9/19 1:42:44 网站建设 项目流程

简介:基于AT89C51单片机的数字音乐盒课程设计报告,适合单片机原理与应用、嵌入式系统开发等课程的期末设计参考,也适合相关专业学生在做课程设计时借鉴。报告从任务书出发,依次完成总体方案论证、硬件电路设计、软件编码与系统调试。硬件部分包括单片机选型、液晶显示、按键控制、复位电路、时钟电路、输出显示和音频输出模块;软件部分给出主程序、外部中断、显示模块、系统初始化、音调节拍编码和流程图设计。资源为单个PDF文档,压缩包共1个文件,大小约1.5MB,已有412人浏览学习。通过阅读这份报告,读者可以理解如何用I/O口产生不同频率方波驱动蜂鸣器演奏乐曲,掌握多曲目切换、暂停、LCD显示播放序号与欢迎信息等功能实现方式,并能参考其中的电路图、程序清单和调试结果更高效地完成自己的设计。

1. 单片机课程设计里的数字音乐盒,先分清要做什么

单片机课程设计里,“基于单片机的数字音乐盒”是出现频率最高的题目之一。它的定位很明确:不是做音频解码,而是用 51 单片机(常见为 STC89C52)的定时器产生音符频率的方波,用无源蜂鸣器把简谱按节拍放出来。整个系统压缩到一颗芯片、一个蜂鸣器、两三个按键就能跑通。

难点集中在三处:音符频率到定时器初值的换算、歌曲数据怎么编码、有源与无源蜂鸣器的驱动方式区别,这三处恰恰是答辩时老师爱追问的点。对课程设计而言,这个题目把定时器中断、键盘扫描、状态切换这些单片机基础串成了一条完整链路。后面按发声原理、硬件电路、C51 程序、仿真验证的顺序展开,参数和代码都能直接套用。

2. 数字音乐盒发声原理:音符频率表与 51 单片机定时器初值计算

音乐盒的本质,是让蜂鸣器在时间轴上按顺序发出不同频率、不同时长的方波。频率由音符决定,时长由节拍决定。动手写代码之前,先把这两张表和一条公式定下来,后面所有程序都是围绕这张表展开的。

2.1 十二平均律下的常用音符频率表与 12MHz 初值

以 A4=440Hz 为基准,课程设计最常用的是从中音 C4 到高音 C6 这一段。下表是数字音乐盒里最常见的音符频率,以及 12MHz 晶振、12T 模式下定时器 0 方式 1(16 位)的初值。

音符频率(Hz)计数个数定时器初值TH0TL0实际频率(Hz)
C42621908636280xF80x8C262.1
D42941700638360xF90x5C294.1
E43301515640210xFA0x15330.0
F43491432641040xFA0x68349.2
G43921275642610xFB0x05392.2
A44401136644000xFB0x90440.1
B44941012645240xFC0x0C494.1
C5523956645800xFC0x44523.0
D5587851646850xFC0xAD587.5
E5659758647780xFD0x0A659.6
F5698716648200xFD0x34698.3
G5784638648980xFD0x82783.7
A5880568649680xFD0xC8880.3
B5988506650300xFE0x06988.1
C61047478650580xFE0x221046.0

这张表的来源是一条公式:12MHz 晶振下 51 单片机是 12T 架构,机器周期固定为 1µs。要产生频率 f 的方波,定时器需要数一个半周期的时间,计数值 = 半周期时间 / 机器周期 = 500000/f。16 位定时器从初值数到 65536 溢出,所以初值 = 65536 - 计数值,TH0 存高位、TL0 存低位。以 C4 为例:500000/262 ≈ 1908,初值 63628,即 0xF88C。取整带来的误差不超过 0.1%,人耳对相对音高的分辨力约 0.5%~1%,整数运算足够。换 11.0592MHz 晶振时,公式要改成 460800/f;STC 芯片烧录时如果设成 6T 模式,机器周期减半,计数值也要跟着减半,否则整首歌会高一个八度。

2.2 有源蜂鸣器与无源蜂鸣器:驱动信号完全不同

数字音乐盒必须选无源蜂鸣器。有源蜂鸣器内部自带振荡电路,通电就响,频率固定不可控;无源蜂鸣器内部没有振荡源,必须由外部提供方波或 PWM 才发声,频率完全取决于驱动信号。这是“单片机控制蜂鸣器”最常见的区分点:

对比项有源蜂鸣器无源蜂鸣器
内部振荡电路
驱动信号直流电平即可方波/PWM
发声频率固定,通常 2~4kHz由方波频率决定
能否播放旋律不能

外观上不好分辨时,用万用表电阻档测:有源蜂鸣器通常有固定阻值,无源蜂鸣器呈线圈特性。Proteus 仿真里也要注意:BUZZER 元件内部就是有源模型,上电即响;无源蜂鸣器对应的是 SOUNDER 元件,需要方波驱动。课程设计里很多人仿真半天不出旋律,就是在这里选错了元件。

2.3 歌曲编码:两张数组把简谱变成音符表

歌曲数据用两张 code 数组表示:一张存音高,一张存拍数。音高数组里放音符在频率表中的下标,拍数数组放对应音符的时值,0xFF 作为结束标志。这样播放逻辑和曲目数据完全分离,换歌只改数组,不用动任何驱动代码。

// 小星星:1 1 5 5 6 6 5 - 4 4 3 3 2 2 1 - // 下标约定:0=C4 1=D4 2=E4 3=F4 4=G4 5=A4 code unsigned char StarNote[] = {0,0,4,4,5,5,4, 3,3,2,2,1,1,0, 0xFF}; code unsigned char StarLen[] = {1,1,1,1,1,1,2, 1,1,1,1,1,1,2, 0xFF}; // 欢乐颂:3 3 4 5 5 4 3 2 | 1 1 2 3 3. 2 2 - code unsigned char OdeNote[] = {2,2,3,4,4,3,2,1, 0,0,1,2,2,1,1, 0xFF}; code unsigned char OdeLen[] = {1,1,1,1,1,1,1,1, 1,1,1,1,2,2,4, 0xFF};

len 数组里的 1、2、4 是相对拍数,不是绝对时间。实际一拍多久由播放函数里的速度参数决定,这样同一首歌可以整体变速。音符下标对应频率表的索引,比直接存频率省 code 空间,也方便扩展休止符:约定下标值大于等于 0x80 时当作休止符处理。两张数组前后必须严格对齐,长度不一致是最容易犯的编码错误。

3. 数字音乐盒硬件搭建:STC89C52 最小系统与无源蜂鸣器驱动

3.1 STC89C52 最小系统:晶振、复位、电源三件套

常见板子以 STC89C52RC 为主,DIP40 封装,与 AT89C52 引脚完全兼容,Proteus 仿真时可以直接放 AT89C52。最小系统三部分缺一不可:

  • 晶振:12MHz 无源晶振接 XTAL1 和 XTAL2,两只引脚各对地接一个 30pF 电容做负载。
  • 复位:RST 引脚对 VCC 接 10µF 电解电容,对 GND 接 10kΩ 电阻,构成上电复位电路。
  • 电源:5V 供电,VCC 与 GND 之间靠近芯片放一个 104 去耦电容。

其中晶振的取值直接决定第二章的频率表能否成立:表格里每个初值都按 12MHz 计算,板子换成 11.0592MHz 后整首歌会整体走音。这是“原理图照着画、上电音不准”的第一原因。

3.2 输出引脚接线与蜂鸣器驱动电路

信号STC89C52 引脚接到哪里
BEEPP2.5(DIP40 第 26 脚)经 1kΩ 电阻到 S8050 基极
KEY1P3.0(第 10 脚)按键到 GND,外接 10kΩ 上拉
KEY2P3.1(第 11 脚)按键到 GND,外接 10kΩ 上拉
XTAL1第 19 脚12MHz 晶振 + 30pF
RST第 9 脚10µF + 10kΩ 复位网络
VCC/GND第 40/20 脚5V / GND + 104 去耦

51 单片机的 P1、P2、P3 是准双向口,高电平拉电流能力只有 1~2mA,直接驱动 20~30mA 的无源蜂鸣器,音量上不去,还会把引脚电平拉低。常见做法是加一个 NPN 三极管 S8050 做开关:蜂鸣器正极接 5V,负极接三极管集电极,发射极接地,基极经 1kΩ 电阻接 P2.5。基极电流约 (5-0.7)/1000 ≈ 4.3mA,三极管进入饱和,集电极压降不到 0.3V,蜂鸣器获得足够的驱动能量。如果改用 ULN2003 这类达林顿管,它的输出是低电平导通,方波逻辑与 NPN 三极管方案正好反相,代码里的翻转逻辑要跟着调整。

// Keil C51 里的引脚定义,与上面的接线表一一对应 sbit BEEP = P2^5; sbit KEY1 = P3^0; sbit KEY2 = P3^1;

3.3 按键、上拉与可选显示

KEY1、KEY2 用独立按键接 GND,配合 10kΩ 外部上拉电阻,平时读到高电平,按下为低电平。51 的 P0 口比较特殊,内部没有上拉,如果后续加数码管显示曲号,P0 必须外接 10kΩ 上拉排阻,否则数码管亮度异常甚至不亮。每个按键的软件消抖放到下一章程序里,用定时器节拍来完成。

3.4 Proteus 仿真元件清单

元件库中的名称参数与注意点
单片机AT89C52与 STC89C52 引脚兼容,可跑全部播放代码
无源蜂鸣器SOUNDER方波驱动,不要用 BUZZER 有源模型
按键BUTTON两个,分别接 P3.0、P3.1
电阻RES1kΩ、10kΩ,数码管方案加 10kΩ 排阻
电容CAP / CAP-ELEC30pF×2、10µF、104
晶振CRYSTAL12MHz,双击元件把频率改成 12M

4. 基于 51 单片机的音乐盒程序实现:定时器中断、歌曲编码与按键换歌

4.1 两个定时器各干一件事

程序用两个定时器:定时器 0 产生音符方波,工作在方式 1(16 位定时),每次中断翻转一次 P2.5,中断间隔就是半个周期;定时器 1 产生 10ms 节拍基准,给音符时长数拍子。两个定时器分工明确,蜂鸣器发声的同时主循环还能扫描按键。

#include <STC89C5x.H> sbit BEEP = P2^5; sbit KEY1 = P3^0; sbit KEY2 = P3^1; // 音符频率表,下标与第 2 章表格一一对应 code unsigned int NoteFreq[] = { 262, 294, 330, 349, 392, 440, 494, 523, 587, 659, 698, 784, 880, 988 }; unsigned char reloadTH, reloadTL; // 当前音符的初值 volatile unsigned char tick10ms; // 定时器1 每 10ms 加 1 void StartTone(unsigned char idx) { unsigned long cnt; TR0 = 0; // 先停表再改初值 cnt = 500000UL / NoteFreq[idx]; // 12MHz/12T,机器周期 1us reloadTH = (unsigned char)((65536UL - cnt) >> 8); reloadTL = (unsigned char)(65536UL - cnt); TR0 = 1; }

这里 500000UL 和 65536UL 是刻意写成 UL 的。Keil C51 的 int 只有 16 位,直接写 500000 和 65536 都会被截断,初值算出来完全不对,这是 Keil 工程里最典型的第一坑。StartTone 先关 TR0 再写初值,是为了避免中断刚好在改初值两个字节之间触发,重装出一组高低位不匹配的值,导致方波频率瞬间跳变。

4.2 两个中断服务函数

void Timer0_ISR(void) interrupt 1 { TH0 = reloadTH; // 手动重装 16 位初值 TL0 = reloadTL; BEEP = ~BEEP; // 翻转输出,产生方波 } void Timer1_ISR(void) interrupt 3 { TH1 = 0xD8; // 10ms 基准:65536-10000=0xD8F0 TL1 = 0xF0; tick10ms++; }

定时器 0 的中断号是 1,定时器 1 的是 3,写错中断号会造成中断入口错乱。12MHz、12T 下定时器 1 数 10000 个机器周期就是 10ms,所以初值 0xD8F0。tick10ms 用 unsigned char 而不是 unsigned int:8 位 MCU 的定时中断里读写 16 位变量存在高低字节撕裂问题,8 位计数器不会有这个问题,代价是单音符时值上限 2550ms,对课程设计完全够用。

4.3 播放函数与主循环:按键换歌、播放

unsigned char beatMs = 40; // 一拍 400ms void PlaySong(unsigned char code *note, unsigned char code *len) { unsigned char i = 0; TR0 = 0; while (note[i] != 0xFF) { if (note[i] < 0x80) // 0x80 及以上当休止符 StartTone(note[i]); tick10ms = 0; while (tick10ms < (unsigned char)(len[i] * beatMs)); // 数拍 TR0 = 0; // 音符结束先静音 i++; } BEEP = 0; } void main(void) { unsigned char songNo = 0; unsigned char t; TMOD = 0x11; // T0、T1 都用方式 1 TH1 = 0xD8; TL1 = 0xF0; // T1 初值 10ms ET0 = 1; ET1 = 1; TR1 = 1; EA = 1; while (1) { if (KEY1 == 0) { // 第一次读到低电平 t = tick10ms; while (tick10ms == t); // 等 10ms if (KEY1 == 0) { // 二次确认,消除抖动 while (KEY1 == 0); // 松手检测 if (songNo == 0) PlaySong(StarNote, StarLen); else PlaySong(OdeNote, OdeLen); } } if (KEY2 == 0) { t = tick10ms; while (tick10ms == t); if (KEY2 == 0) { while (KEY2 == 0); songNo ^= 1; // 切换曲目 } } } }

这段代码的逻辑是:KEY1 按下后等一个 10ms 节拍再确认一次,两次都读到低电平才认为是有效按键,等松手后开始播放;KEY2 同理,只切换 songNo,不打断当前播放。消抖用的是定时器 1 的 tick10ms,而不是空循环 delay,好处是消抖时间不随晶振偏差漂移。播放函数把 tick10ms 清零,再等待 len[i]*beatMs 个节拍,每拍默认 400ms,与歌曲数组里的相对拍数相乘就是绝对时长。整个播放是阻塞式的,按键响应要等当前音符结束,这是课程设计最常用的实现方式,简单、可解释、答辩不虚。

4.4 编译、仿真与烧录流程

  • Keil 里新建工程,芯片选 AT89C52 或直接选 STC89C52RC,Output 选项卡勾上 Create HEX File,编译出 .hex 文件。
  • Proteus 里放 AT89C52、SOUNDER、两个按键和晶振,双击单片机加载 .hex,把晶振频率改成 12MHz,运行仿真。
  • 听不到声音不代表代码错:把示波器探头夹在 P2.5,看到周期正确的方波就说明音符频率对了。SOUNDER 发声依赖本机音频设备,仿真里用示波器验证比用耳朵可靠。
  • 真机烧录用 STC-ISP 软件,型号选 STC89C52RC,串口选 USB 转 TTL 枚举出的 COM 口,点下载后给板子重新上电完成冷启动。烧录界面里的芯片模式必须和频率表一致:默认 12T,选成 6T 会整体高八度。

5. 数字音乐盒音准验证与答辩技巧:示波器、掉电记忆与变速

5.1 三分钟把音准量化

播放 A4 音时,把示波器探头接 P2.5,看到的方波周期应为 1/440 ≈ 2.27ms,误差在 ±2Hz 以内算正常。没有示波器就用手机定音器 App 对着蜂鸣器测,A4 应显示 440Hz。听感上,小星星第一句整体偏闷,先查音符数组下标映射;整首高一个八度,查 STC-ISP 里的 12T/6T 模式;只有个别音飘,查初值取整和晶振误差。

5.2 掉电记住当前曲目:STC 片内 EEPROM

STC89C52RC 内部带数据 Flash,常见例程把起始地址写成 0x2000,按 512 字节一个扇区擦除。换歌时把 songNo 写进去,上电先读出来,答辩时断电再开机回到上一首,是很加分的点。

// 寄存器名以 STC 头文件为准,89 系列常见 ISP_ 前缀 void EEPWrite(unsigned char dat) { ISP_CONTR = 0x83; // 使能 EEPROM,等待周期按 12MHz 选 ISP_CMD = 0x03; // 先擦除扇区 ISP_ADDRH = 0x20; ISP_ADDRL = 0x00; ISP_TRIG = 0x5A; ISP_TRIG = 0xA5; // 触发脉冲,连续写两次 ISP_CMD = 0x02; // 字节编程 ISP_DATA = dat; ISP_ADDRH = 0x20; ISP_ADDRL = 0x00; ISP_TRIG = 0x5A; ISP_TRIG = 0xA5; _nop_(); _nop_(); ISP_CONTR = 0x00; // 关闭,防止误操作 }

写入前必须先擦除所在扇区,擦除粒度是 512 字节,误擦会把整个扇区清成 0xFF。不同批次 STC 芯片的 EEPROM 地址映射可能有差异,上电后先用读功能验证首字节,能读到预期值再决定是否写入。

5.3 让听感上一个台阶的小参数

音符之间留一点静音间隔:在 PlaySong 每个音符结束后、切下一个音符前,让 TR0 置 0 并等一个 10ms tick,能避免蜂鸣器余振把相邻音符连成浑浊一片。变速播放也很简单,把 beatMs 改成全局变量,KEY2 长按在 30/40/50 三档间循环即可。阻塞式播放下新速度只对下一首生效;要做到即时变速,就得把 PlaySong 改造成 10ms 状态机,这是播放器实现的标准做法,也是这个题目向上走一步的方向。

验证项方法通过标准
音准示波器测 P2.5 方波周期A4 对应 2.27ms ±2%
节拍秒表记录 8 拍时长与 beatMs 计算值误差 <0.3s
按键快速连按 20 次无跳变、无卡死
掉电记忆断电再上电恢复上次曲目号

最后盯一眼 STC-ISP 烧录界面里的芯片模式:同一份初值表,12T 模式唱 C4,6T 模式唱出来就是 C5,音高整整升一个八度。先确认模式,再怀疑频率表。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询