恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
51单片机数字音乐盒实战:定时器发声原理、电路与C51程序
首页
资讯中心
/
51单片机数字音乐盒实战:定时器发声原理、电路与C51程序
51单片机数字音乐盒实战:定时器发声原理、电路与C51程序
发布时间:2026/9/19 1:42:45
简介基于AT89C51单片机的数字音乐盒课程设计报告适合单片机原理与应用、嵌入式系统开发等课程的期末设计参考也适合相关专业学生在做课程设计时借鉴。报告从任务书出发依次完成总体方案论证、硬件电路设计、软件编码与系统调试。硬件部分包括单片机选型、液晶显示、按键控制、复位电路、时钟电路、输出显示和音频输出模块软件部分给出主程序、外部中断、显示模块、系统初始化、音调节拍编码和流程图设计。资源为单个PDF文档压缩包共1个文件大小约1.5MB已有412人浏览学习。通过阅读这份报告读者可以理解如何用I/O口产生不同频率方波驱动蜂鸣器演奏乐曲掌握多曲目切换、暂停、LCD显示播放序号与欢迎信息等功能实现方式并能参考其中的电路图、程序清单和调试结果更高效地完成自己的设计。1. 单片机课程设计里的数字音乐盒先分清要做什么单片机课程设计里“基于单片机的数字音乐盒”是出现频率最高的题目之一。它的定位很明确不是做音频解码而是用 51 单片机常见为 STC89C52的定时器产生音符频率的方波用无源蜂鸣器把简谱按节拍放出来。整个系统压缩到一颗芯片、一个蜂鸣器、两三个按键就能跑通。难点集中在三处音符频率到定时器初值的换算、歌曲数据怎么编码、有源与无源蜂鸣器的驱动方式区别这三处恰恰是答辩时老师爱追问的点。对课程设计而言这个题目把定时器中断、键盘扫描、状态切换这些单片机基础串成了一条完整链路。后面按发声原理、硬件电路、C51 程序、仿真验证的顺序展开参数和代码都能直接套用。2. 数字音乐盒发声原理音符频率表与 51 单片机定时器初值计算音乐盒的本质是让蜂鸣器在时间轴上按顺序发出不同频率、不同时长的方波。频率由音符决定时长由节拍决定。动手写代码之前先把这两张表和一条公式定下来后面所有程序都是围绕这张表展开的。2.1 十二平均律下的常用音符频率表与 12MHz 初值以 A4440Hz 为基准课程设计最常用的是从中音 C4 到高音 C6 这一段。下表是数字音乐盒里最常见的音符频率以及 12MHz 晶振、12T 模式下定时器 0 方式 116 位的初值。音符频率(Hz)计数个数定时器初值TH0TL0实际频率(Hz)C42621908636280xF80x8C262.1D42941700638360xF90x5C294.1E43301515640210xFA0x15330.0F43491432641040xFA0x68349.2G43921275642610xFB0x05392.2A44401136644000xFB0x90440.1B44941012645240xFC0x0C494.1C5523956645800xFC0x44523.0D5587851646850xFC0xAD587.5E5659758647780xFD0x0A659.6F5698716648200xFD0x34698.3G5784638648980xFD0x82783.7A5880568649680xFD0xC8880.3B5988506650300xFE0x06988.1C61047478650580xFE0x221046.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/fSTC 芯片烧录时如果设成 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 - // 下标约定0C4 1D4 2E4 3F4 4G4 5A4 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.5DIP40 第 26 脚经 1kΩ 电阻到 S8050 基极KEY1P3.0第 10 脚按键到 GND外接 10kΩ 上拉KEY2P3.1第 11 脚按键到 GND外接 10kΩ 上拉XTAL1第 19 脚12MHz 晶振 30pFRST第 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双击元件把频率改成 12M4. 基于 51 单片机的音乐盒程序实现定时器中断、歌曲编码与按键换歌4.1 两个定时器各干一件事程序用两个定时器定时器 0 产生音符方波工作在方式 116 位定时每次中断翻转一次 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-100000xD8F0 TL1 0xF0; tick10ms; }定时器 0 的中断号是 1定时器 1 的是 3写错中断号会造成中断入口错乱。12MHz、12T 下定时器 1 数 10000 个机器周期就是 10ms所以初值 0xD8F0。tick10ms 用 unsigned char 而不是 unsigned int8 位 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 或直接选 STC89C52RCOutput 选项卡勾上 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 片内 EEPROMSTC89C52RC 内部带数据 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 模式唱 C46T 模式唱出来就是 C5音高整整升一个八度。先确认模式再怀疑频率表。本文还有配套的精品资源点击获取