51单片机播放音乐

📅 2026/7/12 2:39:51
51单片机播放音乐
一、文档概述1.1 项目简介本程序基于标准 51 内核单片机11.0592MHz 晶振通过 IO 口无源蜂鸣器输出方波实现《小星星》乐曲循环播放全程无定时器中断采用纯软件延时翻转 IO 生成不同频率音符结构简单、易移植适合单片机入门音乐播放实验。1.2 硬件环境主控STC89C52/AT89C52 51 单片机晶振11.0592MHz发声器件无源蜂鸣器高电平关闭低电平导通发声IO 分配P1^0驱动蜂鸣器编译环境Keil C511.3 功能特性支持低 / 中 / 高三个八度音阶包含休止符静音功能乐谱采用二维数组存储音符 节拍分离修改曲目便捷纯软件方波生成不依赖定时器中断无中断冲突问题乐曲播放完成自动循环间隔 1 秒重复播放分层函数设计延时、单音符播放、整曲播放、硬件初始化解耦二、头文件与宏、类型定义说明c运行#include reg52.h typedef unsigned char uchar; typedef unsigned int uint; sbit BEEP P1^0; #define REST 0reg52.h51 单片机寄存器标准头文件定义 P0~P3、TCON、TMOD 等特殊功能寄存器类型别名uchar8 位无符号字符、uint16 位无符号整型简化代码书写sbit BEEP P1^0位定义将 P1.0 引脚绑定蜂鸣器控制位无源蜂鸣器默认高电平静音REST休止符标记索引 0代表无音符、静音延时三、音符频率对照表NOTE_TABLE3.1 数组说明NOTE_TABLE为常量数组存储各音阶定时器初值用于换算方波半周期延时索引对应音符表格索引音符音区索引音符音区索引音符音区0REST休止81中音151高音11低音92中音162高音22低音103中音173高音33低音114中音184高音44低音125中音195高音55低音136中音206高音66低音147中音217高音77低音————————————3.2 原理说明数组存储定时器 1 计数初值程序仅提取低 8 位计算半周期延时half_period 256 - (timer_val 0xFF)数值越小延时越短输出频率越高音调越高。四、乐谱数组music_star4.1 存储格式c运行uint code music_star[][2] {{音符索引, 节拍时长ms}, ... , {REST,0}};二维数组第 1 列音符索引取值0~210 为休止符二维数组第 2 列当前音符持续时间单位 ms结束标记{REST,0}循环读取乐谱时以此判定乐曲结束code关键字数组存储在程序 ROM 区不占用 RAM 内存4.2 乐曲逻辑完整《小星星》简谱转译短音 200ms长音 400ms播放完毕延时 1s 后循环重放。五、全局变量说明表格变量名类型作用beat_delayuint存储当前音符节拍时长预留扩展参数timer_valuint临时存储当前音符对应的定时器初值六、底层延时函数6.1void DelayMs(uint ms)功能毫秒级软件延时适配 11.0592MHz 晶振参数ms延时毫秒数实现双层 for 循环空跑固定循环参数j110保证 1ms 基准延时6.2void DelayUs(uint us)功能微秒级短延时用于生成方波高低电平保持时间参数us微秒延时值使用场景蜂鸣器 IO 翻转后延时控制方波频率决定音调高低七、硬件初始化函数Timer1_Init()c运行void Timer1_Init(void) { TMOD | 0x20; ET1 0; TR1 0; }TMOD | 0x20配置定时器 1 为 8 位自动重装模式仅预留本程序未使用定时器中断ET1 0关闭定时器 1 中断允许位彻底屏蔽定时器中断TR1 0关闭定时器 1 计数定时器全程不启动设计目的统一定时器寄存器初始化避免寄存器乱序导致的潜在干扰八、核心播放函数8.1 单音符播放PlayNote(uint note, uint beat)入参note音符索引0~210 为休止符beat音符持续节拍ms执行逻辑休止符判断音符为 REST 时蜂鸣器置高静音直接延时对应节拍函数返回音调计算读取音符对应初值计算方波半周期微秒延时方波循环输出在总节拍时长内循环翻转蜂鸣器 IOBEEP0 发声 → 微秒延时BEEP1 静音 → 微秒延时总时长自减直到节拍耗尽音符播放完成后蜂鸣器置高停止发声8.2 整曲播放PlayMusic(uint music[][2])遍历乐谱二维数组循环读取每组音符节拍判断结束标记music[i][0] REST music[i][1] 0满足则退出循环调用PlayNote()逐音符播放播放完成索引自增读取下一个音符九、主函数main()调用Timer1_Init()完成定时器寄存器初始化BEEP 1上电默认关闭蜂鸣器防止上电杂音无限大循环完整播放《小星星》乐曲延时 1000ms乐曲间隔静音 1 秒重复播放十、程序工作原理无源蜂鸣器发声原理无源蜂鸣器必须输入固定频率方波才能发声高低电平交替翻转产生振动翻转频率决定音调。音调控制不同音符对应不同半周期延时延时越短翻转频率越高音调越高。节拍控制外层总时间循环控制音符持续时长实现长短音区分。静音处理休止符直接拉高蜂鸣器引脚停止方波输出仅做延时。无中断设计优势代码逻辑直观不会与按键、串口等外设中断产生冲突适合初学者调试。十一、修改扩展指南11.1 修改乐曲新增二维code乐谱数组格式{{音符索引,节拍},...,{REST,0}}main函数循环内调用PlayMusic(新数组名)即可切换曲目11.2 调整播放速度统一修改乐谱内所有节拍数值数值变大播放变慢数值变小播放变快或修改主函数循环间隔DelayMs(1000)调整歌曲间隔11.3 更换蜂鸣器 IO修改sbit BEEP P1^0;引脚定义如P2^0、P0^1等硬件同步改接线11.4 适配其他晶振需重新校准DelayMs内层循环值、DelayUs延时基准、NOTE_TABLE初值表。十二、注意事项硬件必须使用无源蜂鸣器有源蜂鸣器无法通过方波调节音调本程序不兼容。蜂鸣器驱动建议增加三极管放大51 单片机 IO 驱动电流不足会导致声音微弱。程序全程阻塞延时播放乐曲时无法响应按键、串口等其他任务如需同步交互需改用定时器中断播放。乐谱数组加code关键字存放在程序存储器减少 RAM 占用去掉后大容量乐谱会导致内存溢出。休止符判断条件严格匹配{REST,0}乐谱末尾必须添加结束标记否则程序数组越界死机。十三、程序优缺点优点逻辑简单无中断、无复杂寄存器操作新手易理解乐谱与程序分离换曲仅需修改数组维护方便资源占用低不占用定时器运行资源移植简单仅需修改 IO 定义与延时参数缺点阻塞式延时播放期间 CPU 无法处理其他任务软件延时精度受晶振、编译器优化影响音准存在微小误差长节拍播放时 CPU 空转资源利用率低#include reg52.h typedef unsigned char uchar; typedef unsigned int uint; // 蜂鸣器IO定义 sbit BEEP P1^0; // 休止符常量 #define REST 0 // 定时器1初值表11.0592MHz仅用来换算半周期延时 uint code NOTE_TABLE[] { 0, // 0:休止符REST 0xF895, // 低音1 0xF960, // 低音2 0xFA09, // 低音3 0xFA90, // 低音4 0xFB00, // 低音5 0xFB68, // 低音6 0xFBC7, // 低音7 0xFC0B, // 中音1 0xFC58, // 中音2 0xFC9C, // 中音3 0xFCCF, // 中音4 0xFCFF, // 中音5 0xFD2E, // 中音6 0xFD59, // 中音7 0xFD7F, // 高音1 0xFDA3, // 高音2 0xFDC4, // 高音3 0xFDDD, // 高音4 0xFDF4, // 高音5 0xFE08, // 高音6 0xFE1A // 高音7 }; // 小星星乐谱 格式{音符索引, 节拍ms} uint code music_star[][2] { {8,200}, {8,200}, {15,200}, {15,200}, {17,200}, {17,200}, {15,400}, {13,200}, {13,200}, {12,200}, {12,200}, {10,200}, {10,200}, {8,400}, {15,200}, {15,200}, {13,200}, {13,200}, {12,200}, {12,200}, {10,400}, {15,200}, {15,200}, {13,200}, {13,200}, {12,200}, {12,200}, {10,400}, {8,200}, {8,200}, {15,200}, {15,200}, {17,200}, {17,200}, {15,400}, {13,200}, {13,200}, {12,200}, {12,200}, {10,200}, {10,200}, {8,400}, {REST,0} // 结束标志 }; uint beat_delay; uint timer_val; // 毫秒延时 11.0592MHz void DelayMs(uint ms) { uint i,j; for(ims; i0; i--) for(j110; j0; j--); } // 微秒级短延时用于生成音符方波 void DelayUs(uint us) { uint i; for(ius; i0; i--); } // 定时器初始化仅配置关闭中断不使用溢出中断 void Timer1_Init(void) { TMOD | 0x20; ET1 0; // 彻底关闭定时器1中断 TR1 0; } // 播放单音符 - 纯软件翻转无中断 void PlayNote(uint note, uint beat) { uint total_time beat; uint half_period; beat_delay beat; // 休止符直接静音延时 if(note REST) { BEEP 1; DelayMs(total_time); return; } // 根据定时初值换算半周期延时 timer_val NOTE_TABLE[note]; half_period 256 - (timer_val 0xFF); // 在节拍总时长内循环翻转蜂鸣器输出方波 while(total_time 0) { BEEP 0; DelayUs(half_period); BEEP 1; DelayUs(half_period); total_time--; } BEEP 1; } // 播放整首乐曲 void PlayMusic(uint music[][2]) { uchar i 0; while(music[i][0] ! REST || music[i][1] ! 0) { PlayNote(music[i][0], music[i][1]); i; } } void main(void) { Timer1_Init(); BEEP 1; // 蜂鸣器默认关闭 while(1) { PlayMusic(music_star); DelayMs(1000); // 歌曲循环间隔1秒 } }