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程序的逻辑。
总结
本次设计只是简单的利用定时器与键盘控制蜂鸣器发出声音,代码可能也有些许问题,后期水平提升了再修改一下吧;本期主要是学习定时器工作原理及使用、数码管显示、矩阵键盘扫描、频率计算等。
热门推荐
情侣间的甜蜜调侃让爱情更有趣,送你10句充满乐趣的调侃短句
湘西钢火烧龙:300年历史的传统民俗活动
贵州石阡温泉一日游:城市喧嚣中的静谧之地
网络文学成文旅“破圈”新载体
俄乌冲突升级,工程兵成战场关键力量
冬季野钓鲫鱼,3个方法提升咬钩量,天寒地冻也能钓鱼
电压与电源适配器:不同国家的探索与实践
嘉峪关三大绝美景观,你打卡了吗?
露娜连招技巧大揭秘:轻松秀翻全场
深圳老人福利升级:公交地铁免费坐,景区门票全免单
宇文邕灭佛:一场轰轰烈烈的宗教改革
荣耀手表2:24小时心率监测+蓝牙通话,冠心病患者健康管理利器
29.22%!中国成人胰岛素抵抗患病率居高,专家解析防治之道
冬季感冒必备:桂枝汤的正确用法
自热火锅和榴莲上不了火车?铁路部门:这些食品携带有限制
绿巨人浩克:电影、游戏、漫画中的多重世界
格力空调E6故障怎么办?这些方法帮你轻松解决
数据预处理从入门到进阶:8大步骤+最新研究进展全解析
从失眠到心血管病,归脾丸的多重医疗价值
大脚趾竟然这么厉害?!
房屋套内面积测量指南:六大空间测量要点与注意事项
羊肚菌选购指南:从外观到包装的全方位检查
有害气体来袭,职场防咳攻略
常吃大蒜和洋葱有啥好处
冬季煮茶:口感更醇厚,养生效果佳
三国志战略版:揭秘三国名将官职变迁
不定积分揭秘质点运动轨迹之谜
湄洲岛春季摄影指南:从油菜花海到海上日出
沪穗营商环境大比拼,企业去哪儿?
洗衣机排水管正确摆放