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程序的逻辑。
总结
本次设计只是简单的利用定时器与键盘控制蜂鸣器发出声音,代码可能也有些许问题,后期水平提升了再修改一下吧;本期主要是学习定时器工作原理及使用、数码管显示、矩阵键盘扫描、频率计算等。
热门推荐
B/S架构与低代码开发平台:Web应用开发的高效解决方案
银行电子银行密码被锁定后如何解锁?
不锈钢阀门材料:304、304L、316、316L的差异与应用
2025年版《中国药典》4003公示稿|药典玻璃瓶内应力测定仪要求
共享单车第十年,你的ofo押金退了吗?
白细胞计数15:背后的意义与应对策略
急性肠胃炎最常见的症状及治疗是什么
三叉神经痛磁共振检查是怎么样的
做肠胃镜检查挂什么科
红外图谱如何分析
PLC系统中有源信号和无源信号的解析与应用
肾和胃有关系吗
三支一扶与西部计划有什么不同?
三支一扶与西部计划有什么不同?
如何正确安装家里的空气开关以确保用电安全?这种安装方式有哪些规范要求?
拍照时怎样运用光线和角度提升照片质量?
被梦见的人会有感应吗?科学、超自然与心理学的解读
睡眠不足会影响学习能力吗
9岁儿童150度近视适合戴什么样的眼镜
项目目标与范围及项目计划演示
隐形眼镜的分类有什么
具有加热还原及干燥除湿功能的密封容器的制作方法
跨越爱情与友情之间的界限:理解复杂的情感关系
墨水行业分析:市场规模、竞争格局与未来趋势
网站建设如何进行有效的数据分析
MES系统工艺管理:全面提升制造业生产效率与质量的关键
如何选择RAID级别
西红柿和圣女果有什么区别?
圣女果和西红柿的区别在哪里?哪个更好吃?
为什么会出现富贵包?