51单片机实现电子琴:定时器音频输出与键盘扫描详解
创作时间:
2025-01-22 01:58:37
作者:
@小白创作中心
51单片机实现电子琴:定时器音频输出与键盘扫描详解
本文将详细介绍如何基于51单片机设计一个简单的电子琴项目。通过定时器产生不同频率的音频信号,并使用矩阵键盘来控制音符的输入。该项目不仅涵盖了硬件连接、代码编写等实践内容,还涉及到了定时器使用、矩阵键盘扫描、音频输出等单片机应用中的重要知识点。
前言
51单片机开发板自带了蜂鸣器,但是无源的,本次设计是通过定时器产生不同的频率来使蜂鸣器产生不同的音高,并让矩阵键盘来控制音高的发生。
一、硬件准备
选择一个51单片机,如AT89C51。
准备一些按键,如4x4矩阵键盘,用于输入音符。
准备一个音频输出设备,如蜂鸣器或小型扬声器,连接到单片机的某个GPIO引脚(这里单片机自带)。
如有需要,还可以添加其他硬件,如LED灯、LCD显示屏等。
二、硬件连接
定义数码管、矩阵键盘、蜂鸣器引脚。
代码如下(示例):
#define GPIO_KEY P1 //定义矩阵键盘I/O口
#define GPIO_DIG P0 //定义数码管I/O口
sbit beep = P2^5; //定义蜂鸣器银胶
三、代码编写
- 初始化GPIO引脚和定时器。
- 编写按键扫描函数,用于检测哪个按键被按下。
- 编写音频输出函数,根据按键值计算音符频率,并使用定时器或PWM产生音频信号。
- 在主循环中调用按键扫描函数和音频输出函数。
(1)以下代码是音符数值,分别给到定时器PL0、PH0赋值,与数码管数字,但是注意是共阴的;
u8 code Highs[24] = {
0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfe,0xfd, //高音 1 2 3 4 5 6 7
0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xff ,0xff };
u8 code Lows[24] = {
0x44,0xfac,0x09,0x34,0x82,0xc7,0x05,0x5c,
0x22,0x56,0x84,0x9a,0xc1,0xe3,0x02,0x11 }; //低音 1 2 3 4 5 6 7
//共阴的表
u8 code shumaguan [24] = { 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,0xe6,
0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,0xe6,
0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,0xe6};
u8 KeyValue;
(2)定时器初始化
void T0Init() //计时器初始化
{
TMOD |= 0x01; //选择工作方式,给TMOD 赋值 ,这里选T0,方式一
ET0 = 1 ; //中断允许:总允许和T0允许
EA = 1;
}
(3)按键扫描
void keyDown()
{
//char a = 0;
GPIO_KEY = 0x0f; //高四位低电平,低四位高电平
if(GPIO_KEY!= 0x0f) //如果有按键按下
{
Delay(5); //延时防抖
if(GPIO_KEY!= 0x0f)
{
//--------------------求被按下的按键在第几列----------------------------//
switch(GPIO_KEY)
{
case(0x07): KeyValue = 0; break; //0000 0111 : 第一列
case(0x0b): KeyValue = 1; break; //0000 1011 : 第二列
case(0x0d): KeyValue = 2; break; //0000 1101 : 第三列
case(0x0e): KeyValue = 3; break; //0000 1110 : 第四列
}
//--------------------求被按下的按键在第几行----------------------------//
GPIO_KEY = 0xf0; //高四位高电平,低四位低电平
switch(GPIO_KEY)
{
case(0x70): KeyValue += 0; break; //第一行
case(0xb0): KeyValue += 4; break;
case(0xd0): KeyValue += 8; break;
case(0xe0): KeyValue += 12; break; //第四行
}
//--------------------------开始计时----------------------------------//
GPIO_DIG = shumaguan[KeyValue]; //共阳数码管,共阴的表,所以要取反
//赋初值
TH0 = Highs[KeyValue];
TL0 = Lows[KeyValue];
//开始计时
TR0=1;
//退出条件:按键松开
while(GPIO_KEY!= 0xf0) // GPIO_KEY!= 0xf0 说明按键还没松,此时CPU始终在此循环中
{
}
}
}
}
(4)主程序将定时器初始化以及按键扫描调用
void main(void)
{
uart_init();
T0Init();
while(1)
{
keyDown();
}
}
(5)定时器中断
oid Timer0() interrupt 1 // T0溢出时引用
{
//退出条件:按键松开
if(!(GPIO_KEY== 0xf0 || GPIO_KEY== 0x0f)) //如果按键还没松开,进入下一个计时
{
TH0 = Highs[KeyValue];
TL0 = Lows[KeyValue];
beep = ~beep;
}
以上代码还缺少一些定义,还有延时函数,下面展示完整代码,如有错误,欢迎指正;
#include <reg51.h>
typedef unsigned char u8;
unsigned char temp;
#define GPIO_KEY P1
#define GPIO_DIG P0
sbit beep = P2^5;
/*******************************全局变量******* *******************/
u8 code Highs[24] = {
0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfe,0xfd, //高音 1 2 3 4 5 6 7
0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xff ,0xff };
u8 code Lows[24] = {
0x44,0xfac,0x09,0x34,0x82,0xc7,0x05,0x5c,
0x22,0x56,0x84,0x9a,0xc1,0xe3,0x02,0x11 }; //低音 1 2 3 4 5 6 7
//共阴的表
u8 code shumaguan [24] = { 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,0xe6,
0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,0xe6,
0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,0xe6};
u8 KeyValue;
/***************************************************************/
void Delay(unsigned int xms)
{
unsigned char i, j;
while(xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
void keyDown()
{
//char a = 0;
GPIO_KEY = 0x0f; //高四位低电平,低四位高电平
if(GPIO_KEY!= 0x0f) //如果有按键按下
{
Delay(5); //延时防抖
if(GPIO_KEY!= 0x0f)
{
//--------------------求被按下的按键在第几列----------------------------//
switch(GPIO_KEY)
{
case(0x07): KeyValue = 0; break; //0000 0111 : 第一列
case(0x0b): KeyValue = 1; break; //0000 1011 : 第二列
case(0x0d): KeyValue = 2; break; //0000 1101 : 第三列
case(0x0e): KeyValue = 3; break; //0000 1110 : 第四列
}
//--------------------求被按下的按键在第几行----------------------------//
GPIO_KEY = 0xf0; //高四位高电平,低四位低电平
switch(GPIO_KEY)
{
case(0x70): KeyValue += 0; break; //第一行
case(0xb0): KeyValue += 4; break;
case(0xd0): KeyValue += 8; break;
case(0xe0): KeyValue += 12; break; //第四行
}
//--------------------------开始计时----------------------------------//
GPIO_DIG = shumaguan[KeyValue]; //共阳数码管,共阴的表,所以要取反
//赋初值
TH0 = Highs[KeyValue];
TL0 = Lows[KeyValue];
//开始计时
TR0=1;
//退出条件:按键松开
while(GPIO_KEY!= 0xf0) // GPIO_KEY!= 0xf0 说明按键还没松,此时CPU始终在此循环中
{
}
}
}
}
void T0Init() //计时器初始化
{
TMOD |= 0x01; //选择工作方式,给TMOD 赋值 ,这里选T0,方式一
ET0 = 1 ; //中断允许:总允许和T0允许
EA = 1;
}
void main(void)
{
T0Init();
while(1)
{
keyDown();
}
}
//计时器0的调用程序
void Timer0() interrupt 1 // T0溢出时引用
{
//退出条件:按键松开
if(!(GPIO_KEY== 0xf0 || GPIO_KEY== 0x0f)) //如果按键还没松开,进入下一个计时
{
TH0 = Highs[KeyValue];
TL0 = Lows[KeyValue];
beep = ~beep;
}
}
四、成果展示
当我们按下矩阵键盘时,按键扫描会扫描是哪个按键,从而调用定义的初值与数码管的数字,这里将他们做了一个绑定,当播放do时数码管显示1,代表它的音名吧,具体可以看按键扫描i程序的逻辑。
总结
本次设计只是简单的利用定时器与键盘控制蜂鸣器发出声音,代码可能也有些许问题,后期水平提升了再修改一下吧;本期主要是学习定时器工作原理及使用、数码管显示、矩阵键盘扫描、频率计算等。
热门推荐
宝宝手掌心发红发热,是生病了吗?什么情况下要去看医生?
木瓜的功效与营养价值:美容养颜、通乳、补充营养
桑乐太阳能安全阀正确使用方法全知道
太阳能热水器漏水问题及预防措施
拔罐后的罐印都暗示啥?哪些人不宜拔火罐?
深度睡眠:健康生活的关键
防尘口罩过滤棉材质有哪些 如何选择防尘口罩过滤棉
左胸刺痛像针扎一样一阵一阵的
早春最宜养肝!肝最喜欢的6个动作,解郁气,活气血,一定要坚持每天做!
孤寡老人遗嘱怎么处理
孤独老人的遗嘱如何执行
老人早上起来头晕恶心呕吐怎么回事
西北风钓鱼最佳钓位和时间
青色芦苇—— 人是一根会思考的芦苇, 用足球来践行教育理念的漫画
渣浆泵使用与维护保养指南
如何查询房屋贷款信息以做出明智决策?这种查询方式有哪些限制?
怎么投诉物业公司有效?一文详解投诉与网上起诉步骤
驾驶证逾期未换证怎么办?考试科目有新变化!
已知功率、电压计算硅碳棒电流
装修必看:家庭常用电线规格与电器功率对照表
营养升级!苹果木耳红枣枸杞豆浆的奇妙益处
枸杞开心果豆浆:营养美味的健康饮品
激光祛斑后适宜食物有哪些
激光祛斑的过程及注意事项
鸿门宴:刘邦与项羽的权谋对决
细数八个世界各地的世纪老藤葡萄园
医生解答:儿童饮用牛奶醪糟的健康风险
牛奶鸡蛋醪糟:兰州人的独家记忆
英国大学人类学专业排名TOP20详情一览 剑桥牛津都是你的选择
申请英国留学大学程序全攻略