☰
51 单片机播放《起风了》:从数字简谱到音符与节拍编码
2026/10/15 4:07:47 网站建设 项目流程

51 单片机播放《起风了》:从数字简谱到音符与节拍编码

1. 项目与实物

这是我学习 51 单片机时完成的蜂鸣器音乐实验,使用普中 A2 开发板。程序通过 Timer0 中断产生方波,用音谱数组控制音高和持续时间,启动后顺序播放一遍参考旋律,结束后停止。

基础驱动与播放框架参考江协科技(原江科大自化协)51 单片机教程。我完成的部分是将《起风了》的参考旋律整理成音符、音区与时值数据;“自己编写音谱数据”指把参考旋律转换成程序数据,不代表原创作曲。!

图:本人使用的普中 A2 开发板。本文是历史学习工程回顾,未在本次整理期间重新进行硬件播放和声音测量。

项目平台与计算条件
开发板普中 A2
配套主控说明按课程配套的 STC89C52RC 说明,Keil 工程目标选择 AT89C52;照片未清晰读出实物芯片丝印
音高与延时计算条件12 MHz 外部晶振、12T,这是原代码采用的计算条件
发声控制P2.5,通过板载发声电路产生声音
开发工具Windows、Keil µVision、C51 工具链
当前功能阻塞式单曲播放,没有暂停、选曲或自动循环

时钟条件要与实际板子区分。普中配套手册提到 12 MHz 和 11.0592 MHz 两种晶振选择,STC89C52RC 也支持 6T/12T;本文按原代码的 12 MHz/12T 条件计算,不把照片中无法读出的参数当作实测结果。更换晶振或时钟模式后,需要重新计算音高表和延时。资料可从普中官方产品资料入口获取,芯片时钟模式见STC 官方说明。

2. 音高:改变定时器溢出间隔

每次 Timer0 中断都将控制引脚翻转一次,两次翻转才组成一个完整方波周期。要产生频率为f的音符,理想的中断间隔是1 / (2f)。

Timer0 使用 16 位模式,并在中断里手动重装计数值。在内部定时、12T、晶振频率为Fosc的条件下,理论计算为:

计数次数 N ≈ Fosc / (12 × 2 × f) 重装值 R = 65536 - N

例如,原表中M1的重装值为64580,计数次数为956。在 12 MHz/12T 条件下,理论输出频率约为523 Hz;该值由代码计算得到,不是示波器实测值。手动重装、中断响应与执行开销也会造成误差。

原中断中的关键代码如下:

/*-------------------------------------------------------------- * 按当前音符重装 Timer0,再翻转蜂鸣器控制引脚。 * 节选自原工程,FreqSelect 必须在频率表有效范围内。 *-------------------------------------------------------------*/if(FreqTable[FreqSelect]){TL0=FreqTable[FreqSelect]%256;TH0=FreqTable[FreqSelect]/256;Buzzer=!Buzzer;}

这里的频率表保存的是重装值,不是以 Hz 为单位的音符频率。Buzzer.c中另外保留了固定频率的发声函数,但音乐主程序实际使用的是上述中断逻辑。

3. 编谱:数字简谱转成音符—时值数据

参考图片属于数字简谱。数字表示音级,数字上方或下方的点区分音区,时值线等符号记录节奏。我将它转换成低、中、高音区宏与时值数据:L7是低音区的 7,M1是中音区的 1,H1是高音区的 1,下划线表示对应的升音。

图:编谱时使用的网络参考简谱,保留原图署名。图片由项目整理者提供并说明可以公开使用;个人贡献为音符与时值数据整理,不将参考谱面标为自己的原创作品。

程序数组按“音符索引、时值单位”成对存储,最后用0xff表示结束。下面只展示格式,不是完整曲目:

/*-------------------------------------------------------------- * 每两个元素为一个事件:音符、时值;0xff 为结束标记。 *-------------------------------------------------------------*/unsignedintcode Music[]={L7,1,M1,1,M2,1,M3,1,0xff};

code是 Keil C51 的存储类型,用来将数组放在程序存储区,并非普通标准 C 的关键字,详见Keil C51 文档。

工程设置SPEED = 500,通过delay(SPEED / 4 * 时值)控制持续时间。按软件延时的设计目标,时值对应关系如下:

时值延时目标相对关系
1125 ms一个基本时间单位
2250 ms两个时间单位
3375 ms三个时间单位
4500 ms若四个单位为一拍,则是一拍
81000 ms按上述约定,则是两拍

软件延时会受到实际时钟、编译结果和发声中断影响,表中不是实测时间。原程序还会在音符后停表并延时约 5 ms,形成短间隔。

参考简谱标注1 = #F、4/4 拍、每分钟 77 拍。原程序直接使用现有频率表和SPEED = 500,不等于精确还原原谱的调号与速度:数字音级还需要映射到正确调性的绝对频率;若四个时值单位为一拍,500 ms 的目标拍长对应约 120 拍/分钟。

仓库保留了个人音符—时值笔记和编谱资料说明。笔记及当前程序各有 285 个事件,但第 189~192 个事件存在差异;本次没有修改它们,复现当前程序以mian.c的Music[]为准,不把原始笔记误当作最终播放数组。

上面的参考谱图与仓库中的个人编谱笔记配合使用,可以对照理解音区、音符顺序与时值编码;本文不为第三方曲目或谱面另行声明代码许可证。

4. 工程运行与后续改进

完整工程位于 GitHub:51 蜂鸣器音乐实验。在 Windows 上使用带 C51 工具链的 Keil 打开project.uvproj,核对硬件参数后构建并下载。主文件保留历史拼写mian.c,不要只改文件名而不更新工程引用。

历史构建日志记录过 0 个错误、3 个未调用函数警告,并生成了 HEX;它只能证明那次构建的情况,不是本次复测结果。本次没有重新构建、播放或测量音高、节拍,因此不提供未验证的精度或稳定性指标。

当前边界后续可以做的实验
阻塞式播放,软件延时控制节拍用定时器节拍和状态机实现非阻塞播放
中断与主循环共享音符索引,原代码有额外的FreqSelect++整理索引更新与范围检查,关注 8 位 MCU 更新 16 位变量的一致性
休止符只是不翻转引脚按实际驱动电路管理静音电平与定时器,验证休止符
音高表、延时注释和工程时钟配置需要核对按真实时钟计算参数,测量方波频率与拍长

这个实验帮助我把简谱符号与程序数据联系起来,也将音高、节拍和播放流程区分开。后续改进会保留实验条件与测量结果,逐步完善为可复现的学习项目。

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

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

立即咨询