ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

51单片机8键电子琴设计:定时器原理、Proteus仿真与实现

51单片机8键电子琴设计:定时器原理、Proteus仿真与实现 简介这套基于51单片机的8键电子琴毕业设计资料面向电子、嵌入式相关专业学生及单片机爱好者适合作为课程设计或毕设参考。内容围绕硬件设计、软件编程、仿真调试等关键环节展开涵盖按键扫描、音符识别、频率生成与PWM音频处理等知识点可帮助理解从原理图到实物制作的全过程。压缩包共57个文件大小2.12MB包含源程序、Proteus仿真文件、原理图Protel/DXP/PDF格式、参考论文、开题报告、元件清单、实物照片及答辩常见问题解答等类型覆盖c、hex、dsn、pdf、doc、jpg等便于按设计流程查阅。已有4986人学习下载。资料中提供Word格式程序与Keil工程源文件可直接编译烧录同时附有程序打开方法、原理图打开方式说明能有效降低上手门槛配套的焊接图、接线图与实物照片也为动手制作和调试排错提供了直观参考。1. 51单片机8键电子琴毕业设计里性价比最高的综合题做毕业设计最怕的不是题目难是又难又没法验证。8键电子琴这个题踩在另一个极端覆盖了 GPIO 输入、定时器中断、方波输出和硬件驱动电路每一部分都在 51 单片机能力圈内Proteus 里拉几个元件就能完整跑通不用等 PCB 打样。电子信息、自动化、物联网专业的学生做课程设计或毕业设计这个题一周内能从零跑到仿真出结果也能往双音、录音回放、自动点歌方向扩展论文的硬件、软件、验证三章都有实打实的数据可写。按原理、原理图、源程序、仿真的顺序把这条最常走的路线完整讲透。2. 音调生成原理51单片机定时器初值与频率对照表2.1 从乐音频率到定时器初值的换算音乐里的每个音高本质是一个固定频率的振动。标准音 A 是 440Hz中央 C 是 262HzC 大调一个八度内 8 个音的频率从 262Hz 排到 523Hz。要让单片机发出指定频率的声音最常见的做法不是用音频芯片而是让一个 I/O 口输出该频率的方波去驱动无源蜂鸣器。剩下的核心问题只有一个如何精确控制方波的周期。控制方波有两条路。第一条是软件延时翻转端口BEEP ~BEEP; DelayHalfPeriod();循环执行。这条路的问题在于延时函数执行时间完全取决于指令周期、Keil 编译优化等级和中断嵌套误差会累积而且延时期间 CPU 干不了任何别的事。第二条是定时器中断翻转Timer0 计数溢出时自动进中断中断里重装初值并翻转一次端口方波半周期就是两次中断的时间间隔由硬件计数保证。8 键电子琴既要发声又要扫按键主循环要让给按键扫描所以选第二条路几乎是必须的。在 12MHz 晶振下51 单片机机器周期是 12 个时钟周期即 1µs定时器每计一个数耗时 1µs。要让方波频率为 f半周期T/2 1/(2f)换算成计数值后定时器初值N 65536 - 1000000 / (2 × f)以 262Hz 为例1000000 / (2 × 262) ≈ 1908N 65536 - 1908 63628十六进制为 0xF88CTH0 填 0xF8TL0 填 0x8C。定时器从 0xF88C 向上计数溢出一次正好 1908µs端口翻转两次形成一个完整方波频率就是 262Hz。这里顺便说一句晶振选型11.0592MHz 晶振是为串口波特率整除准备的做音乐定时常数全是小数答辩现场手算很难看电子琴用 12MHz机器周期正好 1µs所有计算都是整数关系。2.2 8个音初值表放到 code 区而不是 RAMC 大调一个八度的 8 个音按公式逐个算出初始值做成二维数组表。表要放在单片机的 code 区程序存储区加code关键字51 的片内 RAM 只有 128 字节留 16 字节给频率表会跟栈抢空间而且查表比在中断里做除法快得多51 的除法指令动辄消耗几十个周期放进中断会明显拉长中断服务程序。按键唱名频率(Hz)半周期(µs)定时器初值TH0TL0K1do2621908636280xF80x8CK2re2941700638360xF90x5CK3mi3301515640210xFA0x15K4fa3491432641040xFA0x68K5sol3921275642610xFB0x05K6la4401136644000xFB0x90K7si4941012645240xFC0x0CK8do523956645800xFC0x44表里有一个取值细节半周期全部做了四舍五入取整。262Hz 的实际半周期是 1908.4µs取 1908 后实际发声频率约 262.05Hz偏差 0.02%人耳完全分辨不出如果你要把音阶扩展到高八度频率翻倍后取整误差也翻倍就需要用 22.1184MHz 这类更高频率的晶振或者改用自动重装载方式。这个误差来源在答辩时能主动指出来比被问到才解释要主动得多。2.3 为什么选定时器方式1而不是方式251 的定时器有四种工作方式电子琴只用得到方式1和方式2。方式2是 8 位自动重装载初值装入 TH0溢出后硬件自动把 TH0 复制到 TL0中断响应均匀适合做波特率和 PWM。但方式2计数上限只有 256 个机器周期在 12MHz 下最多产生 256µs 的半周期对应最低频率约 1953Hz——C 大调这 8 个音全在 523Hz 以下方式2根本覆盖不了。方式1是 16 位计数器初值范围 0~65535半周期最大 65535µs最低能到 7.6Hz覆盖整个音频范围绰绰有余。代价是溢出后不会自动重装必须手动处理配置代码TMOD 0xF0; /* TMOD高4位属于Timer1先不动它们 */ TMOD | 0x01; /* Timer0工作于方式116位手动重装 */提示很多新手直接写TMOD 0x01把 Timer1 的配置清零了。如果工程里还有串口波特率设置两者会互相踩。手动重装还有一个顺序要求中断服务函数里必须先写 TH0 再写 TL0。因为定时器是 16 位累加结构先写 TL0 再写 TH0 的瞬间如果恰好发生溢出高 8 位会被新值覆盖而低 8 位还是旧值这一个周期就多出或丢掉一个计数示波器上能看到方波周期抖动。另外中断响应本身有 3~8 个机器周期的固定延迟加上重装指令的十几个周期实测频率会比理论值低千分之几这个量级人耳无感但测量时心里要有数。3. 原理图设计8个按键、蜂鸣器与51单片机最小系统的连接方式3.1 最小系统三要素晶振、复位、EA从 51 单片机硬件设计的角度看这个题的原理图不复杂难在元件选型和引脚细节。无论用 AT89C51 还是 STC89C52RC都绕不开晶振、复位、EA 三个环节。晶振电路XTAL119脚和 XTAL218脚之间接 12MHz 晶振两个引脚各用一个 30pF 瓷片电容接地电容值 20~33pF 都行但仿真时单片机模型里的晶振频率必须和这里一致否则后面所有音阶整体跑偏。复位电路RST9脚接 10µF 电解电容到 VCC10kΩ 电阻到 GND构成上电复位想在 RST 和 GND 之间并一个轻触按键做手动复位按下时 RST 被拉高到 VCC。EA31脚接 VCC芯片从片内 ROM 取指这个脚接错是原理图看着对、仿真黑屏最常见的原因。电源部分是实物调试最容易忽略的地方。VCC 引脚旁边并一颗 0.1µF 去耦电容紧贴芯片放置蜂鸣器一响电流抖动就靠它吸收。仿真里这个电容可有可无实物上没有它蜂鸣器启动瞬间可能把单片机拉复位表现就是一按键盘整机重启。P0 口在这个设计里用不到但 P0 是开漏结构、没有内部上拉空闲引脚建议经排阻上拉到 VCCP2、P3 口不用的引脚内部有上拉可以悬空。把空闲 IO 全部定死电平能从源头减少一半的莫名故障。3.2 按键直接映射P1口8键不需要做矩阵8 个按键在这个题里不值得用矩阵扫描。51 单片机矩阵键盘教程里的 4×4 接法是为了用 8 个引脚读 16 个键8 个键直接一对一占满 P1.0~P1.7读一次 P1 口就完成全部扫描。按键一端接 P1.x另一端接 GND利用 P1 口内部上拉松开时读到高电平按下时读到低电平。课程设计标准画法就是 8 个按键一字排开公共端全部接地对应引脚定义/* 原理图对应的引脚定义按键全部接P1口 */ sbit KEY1 P1^0; /* K1 — do */ sbit KEY2 P1^1; /* K2 — re */ sbit KEY3 P1^2; /* K3 — mi */ sbit KEY4 P1^3; /* K4 — fa */ sbit KEY5 P1^4; /* K5 — sol */ sbit KEY6 P1^5; /* K6 — la */ sbit KEY7 P1^6; /* K7 — si */ sbit KEY8 P1^7; /* K8 — do */ sbit BEEP P2^0; /* 蜂鸣器驱动脚 */原理图上还要不要并一排 10kΩ 上拉电阻51 的 P1 口内部上拉已经存在不画外部上拉电路也能工作。但实物上按键引线超过 10cm 时外部电磁干扰可能把引脚拉出毛刺。我的做法是仿真图不加上拉实物图加一排 10kΩ 排阻到 VCC成本几毛钱答辩被问为什么加时解释成增强抗干扰比回答别人都画了体面得多。按键消抖可以在每个键上并 0.1µF 电容到 GND 做硬件消抖也可以不加电容把消抖放程序里两者取其一不要叠加否则按键响应延迟会叠加到十几毫秒。3.3 发声器件无源蜂鸣器加三极管驱动发声器件是原理图里最容易被坑的一环先下结论电子琴必须用无源蜂鸣器不能有源。有源蜂鸣器内部自带振荡电路通电就以固定频率通常 2.7kHz 左右发声只能当报警器无法通过改变输入频率改变音高。无源蜂鸣器内部没有振荡源靠外部方波驱动频率完全由程序决定才能弹曲子。无源蜂鸣器工作电流在 20~30mA51 单片机 P2 口是准双向口直接驱动感性负载有风险Proteus 里直接接蜂鸣器模型也可能因为驱动能力问题不响。标准接法是 PNP 三极管低电平驱动VCC ── 蜂鸣器正极 蜂鸣器负极 ── S8550 集电极 S8550 发射极 ── GND S8550 基极 ── 10kΩ电阻 ── P2.0P2.0 输出低电平时三极管导通蜂鸣器得电输出高电平时截止蜂鸣器断电。注意这里逻辑是低电平发声跟直觉相反程序里静音时要输出高电平而不是清零。基极 10kΩ 电阻是限流保护省略后基极电流可能超过 P2 口驱动极限。Proteus 里如果找不到 S8550可以用 BC327 替代参数兼容。元件清单按下面这张表备料Proteus 对应的元件名一并列出元件参数/型号数量Proteus关键字备注单片机AT89C51 / STC89C52RC1AT89C51仿真首选AT89C51晶振12MHz1CRYSTAL属性里确认12M瓷片电容30pF2CAP晶振负载电解电容10µF1CAP-ELEC复位电阻10kΩ2RES复位基极轻触按键6×68BUTTON按下接地无源蜂鸣器5V1BUZZER必须无源三极管S85501BC327低电平导通4. 源程序实现Keil5下按键扫描与定时器中断发声的完整代码4.1 程序结构中断负责发声主循环负责扫描整个程序只需要三个部分定时器初始化、主循环按键扫描、定时器中断服务。设计原则是各干各的——中断里绝不做按键扫描主循环里绝不做延时翻转。按键扫描需要消抖延时放进中断会拖慢发声周期发声需要精确的定时器初值放在主循环会被扫描打断。全局变量 CurrentKey 负责两个模块间的通信。下面这份代码是 Keil5 下可以直接编译的完整工程芯片选 AT89C51 或 STC89C52RC 都行头文件 reg52.h 通用#include reg52.h sbit BEEP P2^0; /* 蜂鸣器控制脚低电平驱动 */ #define KEY_PORT P1 /* 8个按键接P1.0~P1.7 */ unsigned char CurrentKey 0xFF; /* 当前按下键索引0xFF无键 */ /* 每个音对应的TH0/TL0初值对应do re mi fa sol la si do */ unsigned char code ToneTable[8][2] { {0xF8, 0x8C}, /* do 262Hz */ {0xF9, 0x5C}, /* re 294Hz */ {0xFA, 0x15}, /* mi 330Hz */ {0xFA, 0x68}, /* fa 349Hz */ {0xFB, 0x05}, /* sol 392Hz */ {0xFB, 0x90}, /* la 440Hz */ {0xFC, 0x0C}, /* si 494Hz */ {0xFC, 0x44} /* do 523Hz */ }; void Timer0_Init(void) { TMOD 0xF0; /* 只改Timer0相关位 */ TMOD | 0x01; /* Timer0方式116位 */ TH0 ToneTable[0][0]; /* 先装do的频率初值 */ TL0 ToneTable[0][1]; ET0 1; /* 允许Timer0中断 */ EA 1; /* 开总中断 */ TR0 1; /* 启动定时器 */ } void main(void) { unsigned char i; Timer0_Init(); while (1) { for (i 0; i 8; i) { if ((KEY_PORT (0x01 i)) 0x00) { CurrentKey i; /* 记录被按下的键 */ break; } } if (i 8) /* 全部扫描完都没按下 */ CurrentKey 0xFF; /* 标记静音 */ } } void Timer0_ISR(void) interrupt 1 { if (CurrentKey 8) /* 有键按下才翻转 */ { TH0 ToneTable[CurrentKey][0]; /* 先重装高字节 */ TL0 ToneTable[CurrentKey][1]; /* 再装低字节 */ BEEP ~BEEP; /* 翻转输出产生方波 */ } else { BEEP 1; /* 高电平让三极管截止静音 */ } }代码逻辑上的四个要点。第一按键判断用位与运算0x01 i生成一个只有第 i 位为 1 的掩码P1 口对应位为 0 表示该键按下位与结果为 0 就命中。第二CurrentKey 是 8 位变量51 单片机对它的读写在单条指令内完成主循环和中断之间不会出现数据撕裂一旦换成 16 位变量就必须考虑关中断保护这是做扩展时最容易踩的坑。第三中断里先重装初值再翻转顺序不能反原理在 2.3 节说过。第四code关键字把频率表放进程序存储区编译后 .hex 文件里能直接看到这些字节烧录进单片机后 RAM 压力很小。主循环里 for 循环扫完 8 个键大约十几个指令周期比按键的机械响应快几个数量级所以轮询扫不完的担心不成立。4.2 按键消抖的三种做法与取舍上面这份代码在 Proteus 仿真里完全正常实物上却会出现按一下响两声。机械按键触点闭合瞬间有 5~10ms 的抖动程序必须消抖。三种常见做法的对比消抖方式实现优点缺点软件延时检测到低电平后延时10ms再确认代码简单阻塞主循环定时扫描每5ms扫描一次连续两次一致才确认不阻塞响应均匀需要状态变量硬件电容按键并联0.1µF电容不占CPU增加元件课程设计最常用的软件延时写法if ((KEY_PORT (0x01 i)) 0x00) { Delay10ms(); /* 等待抖动过去 */ if ((KEY_PORT (0x01 i)) 0x00) CurrentKey i; /* 二次确认确实按下 */ }Delay10ms 用双层循环实现在 12MHz、Keil 默认优化等级下大约十万条指令能凑出 10ms但我建议写完后用示波器量一下实测延时因为优化等级从 O0 改成 O2延时时间可能缩水到 4ms。定时扫描方式更稳用一个 5ms 的 Timer1 中断做扫描节拍相邻两次读到相同状态才更新 CurrentKey消抖和扫描合一发声不受阻塞。答辩时能画出消抖前后按键波形对比是这个题最容易拿分的点。4.3 扩展方向自动演奏与矩阵键盘自动演奏是 8 键电子琴最自然的扩展方向。把旋律的音符索引和节拍时长分别存成两个数组用 Timer1 做节拍定时到点就从数组里取下一个音符写入 CurrentKey蜂鸣器自动响完一首歌unsigned char code Melody[] {0,0,4,4,5,5,4,0xFF}; /* 小星星前两句 */ unsigned char code Beat[] {1,1,1,1,1,1,2,0xFF}; /* 11拍,22拍 */0xFF 做结束标志主循环解析到结束标志就停止播放。节拍长度用 Timer1 中断累计实现比软件延时精确得多。这个扩展对论文的价值在于把定时器的两个通道都用起来还能顺带讲音乐数据的编码方式新增代码不到 30 行。如果题目要求做成矩阵键盘版本比如扩展到 16 键或 4×4 布局扫描逻辑要变成两段——逐行拉低、逐列读入——但核心仍然是扫描到键→映射成音阶索引→更新 CurrentKey。发声部分代码一行都不用改这是把扫描和发声分层设计带来的最大好处。5. Proteus仿真调音与实物验证的3个实用技巧5.1 Proteus仿真的三个坑仿真工程建完后双击单片机元件在 Program File 里加载 Keil5 编译生成的 .hex 文件把 Clock Frequency 确认在 12MHz。最常见的三个坑按出现频率排第一蜂鸣器模型选错。Proteus 元件库里搜 BUZZER 会同时出现有源和无源模型选错的表现是上电就一直响按键没反应。无源蜂鸣器在 Proteus 里也常写成 SOUNDER拿不准就先用一个 1kΩ 电阻做负载确认 P2.0 有方波输出后再说。第二晶振频率没同步。原理图画了 12MHz但仿真模型内部时钟是默认值没改音高整体跑偏改一处属性就解决。第三仿真发散这类模拟量报错大多出现在蜂鸣器被当成模拟器件、数学求解不收敛时把蜂鸣器属性里的 Spice model 禁用、按数字逻辑器件参与仿真即可。5.2 用虚拟示波器给每个音测频率Proteus 左侧工具栏的 Virtual Instruments 里拖一台 OSCILLOSCOPE探针接 P2.0GND 接地。按下 K1 键屏幕上方出现方波用光标功能量取 10 个完整周期再除以 10得到一个单周期时长取倒数就是实测频率。按表里 262Hz 算周期约 3.816ms10 个周期约 38.16ms。因为中断延迟的存在实测频率会比表内理论值低千分之几这是正常现象不是程序 bug。这个方法能同时确认三件事频率表没抄错、按键扫描能正确更新 CurrentKey、三极管逻辑是低电平发声而不是反了。如果手边有 COUNTER TIMER 虚拟仪器接在 P2.0 上能直接读频率但看不到波形占空比和抖动答辩演示建议示波器出图、计数器出数两个都放上。5.3 实物调试不靠耳朵靠数据和工具实物板子做好后耳朵能听出音阶顺不顺但听不出 262Hz 和 270Hz 的差别。手机装一个调音器 App长按每个键看读数每个音应该落在 C4、D4、E4、F4、G4、A4、B4、C5 上频偏条好于 ±5 音分算合格。如果所有音整体偏差一致优先怀疑晶振标称频率和实际频率的差异只有个别音偏检查频率表对应行的初值取整有没有算错。调试时蜂鸣器声音偏小不要下意识怪驱动电路先用万用表确认导通时蜂鸣器两端电压接近 4.5VS8550 饱和压降约 0.3V5V 供电下蜂鸣器得到约 4.7V。最后把示波器光标放到 10 个周期上读总时长再除以 10得到的频率误差能控制在 0.1% 以内这个读数可以直接写进论文的实验数据。本文还有配套的精品资源点击获取
返回列表