简介:这份资源是面向电子信息、自动化等专业学生及单片机初学者的课程设计报告文档,围绕AT89S52单片机多音阶电子琴的完整设计展开,可帮助读者理解从需求分析到软硬件实现的课程设计全流程。压缩包内共1个doc文件,约488KB,内容涵盖任务与设计要求、系统总体框图、硬件电路设计、元件清单、程序流程图及主要功能程序设计等章节,并附有音符频率与定时器计数值对照表。报告详细说明了中心控制模块、按键控制模块、声音播放模块与复位按键模块的组成,以及利用定时/计数器T0产生不同频率方波实现音阶输出的原理,还给出LM386音频放大电路与16按键输入的具体方案。已有211人学习,适合作为课程设计写作模板与单片机音频控制入门参考,便于读者对照梳理硬件连接、程序流程与参数计算方法。
1. 从“按下去不响”说起:这套多音阶电子琴方案到底解决什么
课程设计最怕的不是写不出代码,而是仿真跑起来喇叭一声不吭。基于 AT89S52 的多音阶电子琴,表面看是“按键发声”的小玩意,真正卡人的地方在于定时器初值算错一位、按键扫描漏了消抖、音频放大电路偏置没调对,任何一个环节翻车都会让整块板子变成哑巴。这套方案要解决的就是把“音阶频率—定时器计数值—方波输出—音频放大”这条链路一次打通,给出可复现的硬件连接、软件流程和仿真调试路径。它适合正在做单片机课程设计、需要交一份完整报告的学生,也适合想快速搭一个音频输出原型的从业者。整套设计围绕 AT89S52 展开,P2 口读 16 个按键,P1.0 输出方波,经 LM386 放大后驱动喇叭,两个定时器分工:T0 管音高,T1 管节拍。
2. 需求与选型:为什么是 AT89S52 加双定时器结构
2.1 功能需求拆解成可验收的条目
动手画原理图之前,先把“电子琴”这个模糊需求拆成能逐条打勾的验收项,否则做到一半会发现不知道该测什么。这套设计的功能边界通常这样划定:16 个按键中 7 个对应中音段音符(Do 到 Si),1 个作为模式切换键,其余按键预留给自动播放曲目的触发或扩展音区;按下音符键立即发声,松开即停;模式键触发外部中断,切换到预存曲目自动播放;播放过程中再按模式键可回到手动弹奏。音高覆盖低音、中音、高音三个八度,节拍支持 4/4、3/4、2/4 三种曲调值。
把这些写成验收表,后面调试就有对照:
| 验收项 | 判定标准 | 对应模块 |
|---|---|---|
| 单键发声 | 按下任一音符键,喇叭输出对应频率方波 | 按键扫描 + T0 |
| 音阶准确 | 中音 Do 实测约 523Hz,误差在听感可接受范围 | 定时器初值表 |
| 模式切换 | 模式键按下后进入自动播放,再按返回手动 | 外部中断 |
| 节拍可控 | 自动播放时音符时长符合曲调值设定 | T1 定时 |
| 音量可闻 | LM386 输出驱动喇叭,无明显失真 | 音频放大 |
这张表看着朴素,但它决定了后面每一段代码和每一根连线为什么存在。很多报告写不下去,就是因为需求阶段只写了“设计一个电子琴”,没有落到可测的条目上。
2.2 主控与外围器件的选型理由
选 AT89S52 不是因为它多先进,而是课程设计场景下它最“稳”:8051 内核资料齐全,12MHz 晶振下机器周期 1μs,定时器初值计算有成熟公式可循,P1、P2、P3 口够用,ISP 下载也方便。相比同系列的 AT89C52,S 版本支持在线编程,烧录环节少一个编程器,对实验室环境更友好。
外围器件清单对应关系如下:AT89S52 一片作为中心控制;16 个轻触按键分四组接 P3 的低四位配合 P3.4~P3.7 做行扫描;30pF 瓷片电容两只配 12MHz 晶振构成时钟电路;LM386 一片做音频功率放大;8Ω 喇叭一只;复位电路用按键加阻容;电阻电容若干做限流、滤波和偏置。这里有个容易被忽略的点:LM386 的增益默认是 20 倍,如果喇叭声音偏小,在 1 脚和 8 脚之间串一个 10μF 电容可以把增益提到 200 倍,但增益太高会引入底噪,课程设计里通常保持默认或加小容量电容折中。
2.3 双定时器分工的设计思路
整个方案的核心设计决策是把“音高”和“节拍”拆给两个定时器,而不是用一个定时器硬扛。T0 工作在模式 1(16 位定时),负责按当前音符的频率不断溢出并翻转 P1.0,产生方波;T1 同样工作在模式 1,负责计时音符持续时间,溢出后切换到下一个音符。这样拆的好处是音高和节奏互不干扰,改节拍不用动音阶表,改音阶也不用碰节拍逻辑。
音高产生的原理是:某个音符频率为 fr,周期 T=1/fr,半周期就是方波翻转的间隔。T0 每次计到半周期就溢出,中断里重装初值并翻转 P1.0,输出端就得到频率为 fr 的方波。初值公式为:
N = fi / 2 / fr T = 65536 - N = 65536 - fi / 2 / fr其中 fi 是机器频率,12MHz 晶振下为 1MHz。以中音 Do(523Hz)为例,T = 65536 - 1000000/2/523 ≈ 64580。这个值就是写进 TH0/TL0 的初值。低音 Do(262Hz)算出来约 63628,高音 Do(1046Hz)约 65058。所有音符的初值预先算好做成表,程序运行时查表赋值,这是这套方案最“可抄”的部分。
3. 硬件设计:四模块划分与关键电路落地
3.1 总体框图与模块职责
系统按功能划成四块:中心控制模块、按键控制模块、声音播放模块、复位与时钟模块。中心控制模块以 AT89S52 为核心,P2 口配置为输入读取按键状态,P1.0 作为方波输出脚接音频放大,P3 口部分引脚用于按键行扫描和模式键中断输入。按键模块提供 16 个输入通道,其中 7 个映射到中音段音符,1 个映射模式切换,其余留作扩展。播放模块从 P1.0 取信号,经 LM386 放大后推喇叭。复位模块保证上电和手动复位时程序从 0000H 重新开始。
框图用文字描述就是一条主链加两条支链:主链是“按键 → P2 口 → 单片机识别 → 查表 → T0 输出方波 → P1.0 → LM386 → 喇叭”;支链一是“模式键 → 外部中断 → 切换手动/自动”;支链二是“复位电路 → RST 引脚 → 程序复位”。画原理图时按这条链走,信号流向一目了然,报告里的框图说明也能直接对应。
3.2 按键扫描电路与端口分配
16 个按键如果全占一个口,P2 的 8 位不够用,所以采用行扫描思路:P3.4~P3.7 轮流输出低电平作为行选,P3.0~P3.3 读列状态。每一轮只拉低一行,读四列,四行扫完就是 16 个键位。这种接法的好处是占用引脚少,代价是程序里要多一层扫描循环,并且必须做消抖。
消抖不能省。机械按键按下瞬间会有 5~20ms 的抖动,直接读会一次按下被识别成多次。通常的做法是检测到低电平后延时 10ms 再读一次,两次一致才确认。下面这段扫描逻辑是这套方案里最常被照搬的骨架:
// 行扫描读键,返回键值 0~15,无键按下返回 0xFF unsigned char key_scan(void) { unsigned char row, col, key = 0xFF; for (row = 0; row < 4; row++) { P3 = 0xFF; // 先全部置高 P3 &= ~(0x10 << row); // 拉低当前行 P3.4~P3.7 col = P3 & 0x0F; // 读列 P3.0~P3.3 if (col != 0x0F) { // 有列被拉低,说明有键按下 delay_10ms(); // 消抖 col = P3 & 0x0F; if (col != 0x0F) { key = row * 4 + col_to_index(col); while ((P3 & 0x0F) != 0x0F); // 等待松手 } } } return key; }这段代码里0x10 << row控制行选引脚,col_to_index把列位映射成 0~3 的序号,row * 4 + 列号得到 0~15 的键值。等待松手的while循环是防止长按被重复触发,实际项目里如果要做“按住连发”效果,这里要改成带超时的逻辑。参数上唯一要调的是消抖延时,10ms 对大多数轻触键够用,按键偏硬可以加到 15ms。
3.3 音频输出与 LM386 放大电路
P1.0 输出的是 0/5V 方波,直接接喇叭声音极小且波形畸变,必须经过放大。LM386 接成典型音频放大电路:输入端经 10kΩ 电位器分压后接 3 脚,2 脚接地,6 脚接 5V,4 脚接地,5 脚经 220μF 耦合电容接喇叭,7 脚经 10μF 电容接地做旁路。电位器的作用是调音量,课程设计里这个细节很加分,因为答辩时老师常问“音量怎么控制”。
这里有个血泪经验:LM386 的电源脚一定要就近加 0.1μF 去耦电容,否则方波的高频分量会串到电源上,喇叭里会听到“滋滋”的底噪,仿真里看不出来,实物一焊就暴露。另外 P1.0 到 LM386 输入之间串一个 1kΩ 电阻限流,避免单片机引脚被后级拉得太狠。
3.4 复位与时钟电路的参数
复位电路用 10μF 电容加 10kΩ 电阻构成上电复位,再并一个按键做手动复位。上电瞬间电容未充电,RST 为高,随着电容充电 RST 下降,高电平持续时间约等于 RC 时间常数,10μF×10kΩ=100ms,满足 8051 要求的两机器周期以上。时钟电路就是 12MHz 晶振跨接 XTAL1、XTAL2,两端各接一只 30pF 电容到地。晶振频率直接决定机器周期,进而决定所有定时器初值,所以这个 12MHz 不能随便换,换了整张音阶表都要重算。
4. 软件设计:查表发声与双定时器协同
4.1 音阶频率表与查表机制
软件的第一个核心是音阶表。把低音、中音、高音三个八度共 21 个音符(含必要的半音)的定时器初值算好,按顺序存成常量数组,程序根据键值算出表索引,取出 TH0、TL0 初值。表的结构通常按“低音 0~6、中音 7~13、高音 14~20”排,键值到索引的映射在按键处理里完成。
// 12MHz 晶振,T0 模式1,各音符定时器初值(十六进制) // 顺序:低音Do~Si,中音Do~Si,高音Do~Si unsigned int code note_table[] = { 0xF88C, 0xF95B, 0xFA15, 0xFAC7, 0xFB64, 0xFC0C, 0xFC44, // 低音 0xFC44, 0xFCAC, 0xFD09, 0xFD34, 0xFD82, 0xFDC8, 0xFE06, // 中音 0xFE06, 0xFE56, 0xFE85, 0xFEB2, 0xFED2, 0xFEF9, 0xFF23 // 高音 }; // 根据键值取初值并启动 T0 void play_note(unsigned char key) { unsigned int t = note_table[key]; TH0 = (unsigned char)(t >> 8); // 高字节 TL0 = (unsigned char)(t & 0xFF); // 低字节 TR0 = 1; // 启动定时器 }表里的十六进制值就是前面公式算出的十进制初值转换而来,比如中音 Do 的 64580 对应 0xFC44。code关键字把表放进程序存储器,节省 RAM。play_note里先写 TH0 再写 TL0 的顺序不能反,8051 的 16 位定时器是分两次写入的,顺序错了初值会错位,这是仿真里“音不准”的头号原因。
4.2 T0 中断服务与方波翻转
T0 溢出中断是发声的执行者。中断里做三件事:重装初值、翻转 P1.0、返回。重装初值必须用预先存好的变量,不能在中断里重新查表,否则中断响应时间变长,高频音符会失真。
// T0 中断:重装初值并翻转输出脚,产生方波 void timer0_isr(void) interrupt 1 { TH0 = reload_h; // 重装高字节 TL0 = reload_l; // 重装低字节 P1_0 = ~P1_0; // 翻转 P1.0,形成方波 }reload_h、reload_l在play_note里同步更新,中断只负责搬运。翻转用位操作而不是整口取反,避免影响 P1 口其他引脚。方波的占空比固定 50%,因为半周期翻转一次,这也是为什么初值公式里要除以 2。
4.3 T1 节拍控制与自动播放
自动播放模式下,T1 负责音符时长。每个音符除了音高,还有一个节拍值,4/4 拍的一个四分音符约 125ms,3/4 拍约 187ms,2/4 拍约 250ms,这些值对应不同的 T1 初值。播放流程是:从曲谱数组取出第一个音符的音高和节拍,启动 T0 发声、T1 计时,T1 溢出后停 T0、取下一个音符,直到曲谱结束。
// 曲谱数组:每两个字节一组,高字节为音符索引,低字节为节拍码 unsigned char code song[] = { 7, 1, 9, 1, 11, 1, 12, 2, // 示例:中音Do Re Mi Fa,节拍码1为四分音符 11, 1, 9, 1, 7, 2, 0xFF // 0xFF 作为结束标志 };节拍码到 T1 初值的换算做成小表,播放时查表赋值。这里的设计要点是 T1 中断里只做“停 T0、指针加二、判断结束、取下一音符”,真正的发声启动放在主循环或状态机里,避免中断嵌套过深。很多同学把整套播放逻辑塞进 T1 中断,结果节拍一快就乱,原因就是中断里干了太多活。
4.4 模式切换的外部中断处理
模式键接在 INT0 或 INT1 上,配置为下降沿触发。中断服务里只置一个标志位,主循环检测到标志位再切换状态,这叫“中断快进快出”。如果直接在中断里做模式切换和曲谱初始化,中断执行时间过长,正在发声的 T0 会被耽误,听感上就是切换瞬间“卡一下”。
bit auto_mode = 0; // 0 手动,1 自动 bit mode_flag = 0; // 模式切换请求标志 void int0_isr(void) interrupt 0 { mode_flag = 1; // 只置标志,不做事 } // 主循环里处理 if (mode_flag) { mode_flag = 0; auto_mode = ~auto_mode; if (auto_mode) { song_index = 0; start_song(); } else { TR0 = 0; TR1 = 0; } // 回手动前先停定时器 }这段逻辑的关键是切换前先关定时器,否则残留的 T0 中断会继续翻转 P1.0,出现“松手了还在响”的怪现象。
5. 避坑与排查:仿真不响、音不准、按键乱跳的常见问题
5.1 仿真里按键有反应但喇叭不响
现象是 Proteus 里按键状态能读到,P1.0 也有波形,但喇叭静音。原因通常有两个:一是 LM386 的电源和地没接或接错,仿真模型不工作;二是喇叭属性里的阻抗设得过大,或者没有把喇叭接到 LM386 的 5 脚输出上。解决方法是先查 LM386 的 6 脚是否接 5V、4 脚是否接地,再用示波器探针看 5 脚有没有放大后的波形,有波形没声音就是喇叭参数问题,改 8Ω 再跑。
5.2 音阶频率整体偏高或偏低
现象是每个音都“跑调”,但相对关系对。原因基本锁定在晶振频率设置和初值表不匹配:Proteus 里单片机属性默认晶振可能是 12MHz,但代码里的表是按别的频率算的,或者 Keil 里没把晶振频率设成 12MHz 导致延时函数偏差。解决方法是统一三处频率设置——Proteus 单片机属性、Keil 目标选项里的 Xtal 值、初值计算公式里的 fi,三处必须一致。改完重算整张表,不要只改一两个值。
5.3 按一个键触发多个音符
现象是手指按一个键,喇叭里连响好几个音。原因是按键扫描没做消抖或消抖时间太短,抖动被当成多次按下。解决方法是把消抖延时加到 10~15ms,并且确认“等待松手”的循环真的在等列状态恢复高电平。还有一种隐蔽情况:行扫描时上一行的电平没及时恢复,导致下一行读到残留状态,检查P3 = 0xFF这句是否在每轮扫描开头都执行了。
5.4 自动播放节拍忽快忽慢
现象是曲目播放时音符时长不稳定。原因多是 T1 中断里做了查表、判断、启动 T0 等耗时操作,中断执行时间随分支变化。解决办法是把 T1 中断精简成“置节拍到标志”,主循环看到标志再推进曲谱指针。另一个常见原因是 T0 和 T1 中断优先级没设,T1 打断 T0 的重装过程导致方波周期抖动,给 T0 设高优先级可以缓解。
5.5 复位后程序不从头执行
现象是按下复位键,音乐停了一下又接着原来的地方播。原因是复位电路的高电平持续时间不够,或者程序里没有在初始化阶段清状态变量。检查复位电容是否虚焊、RC 值是否够 100ms,同时在main开头把所有状态标志、曲谱指针、定时器控制位显式清零,不要依赖上电默认值。
6. 验收与答辩:怎么用一张测试表把方案讲清楚
验收这套电子琴,我一般按“静态查连接、动态测波形、听感验音阶、流程走模式”四步走。静态阶段对照原理图逐点量通断,重点看 P1.0 到 LM386 输入、LM386 输出到喇叭、晶振两脚到电容这几段。动态阶段用示波器或 Proteus 虚拟示波器看 P1.0 波形,按中音 Do 应该看到约 523Hz 方波,占空比接近 50%,波形上升沿干净无毛刺。听感阶段把低音、中音、高音各弹一遍,对照音阶表确认没有明显跑调。流程阶段完整走一遍“手动弹奏 → 模式键 → 自动播放 → 再按模式键 → 回手动”,确认切换无卡顿、无残留发声。
答辩时老师最爱问的三个问题,提前准备好答案:第一,“音阶频率怎么保证准确”,答定时器初值公式加 12MHz 晶振,并现场指出表里中音 Do 的 64580 是怎么算的;第二,“两个定时器为什么这么分工”,答 T0 管音高保证波形连续,T1 管节拍保证节奏独立,互不干扰;第三,“按键消抖怎么做的”,答 10ms 延时二次确认加等待松手。把这三个点讲透,基本就稳了。
下面这张测试记录表可以直接印出来填,答辩时往报告里一贴,比大段文字有说服力:
| 测试项 | 操作 | 预期现象 | 实测记录 |
|---|---|---|---|
| 中音 Do | 按对应键 | P1.0 约 523Hz 方波,喇叭发声 | |
| 高音 Do | 按对应键 | 频率约翻倍,音调明显升高 | |
| 模式切换 | 按模式键 | 进入自动播放,再按返回 | |
| 节拍 4/4 | 播放曲目 | 四分音符约 125ms | |
| 复位 | 按复位键 | 播放停止,回到初始状态 |
有个习惯我从做第一个单片机课设保持到现在:不管仿真多顺利,焊板子之前一定先把定时器初值表用计算器逐个复核一遍,再把 P1.0 单独接一个 LED 跑一遍“按一下闪一下”的最小测试。听起来笨,但它能把“代码逻辑错”和“硬件连接错”提前分开,省下大量在实验室熬夜排查的时间。这套电子琴方案真正值钱的地方不在那几十行中断代码,而在“需求拆成验收项、模块划清职责、每个参数有出处”的做事顺序。希望帮到你。
本文还有配套的精品资源,点击获取