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程序的逻辑。
总结
本次设计只是简单的利用定时器与键盘控制蜂鸣器发出声音,代码可能也有些许问题,后期水平提升了再修改一下吧;本期主要是学习定时器工作原理及使用、数码管显示、矩阵键盘扫描、频率计算等。
热门推荐
老君山出游攻略
关于“借名买车”,你需要了解这些风险!
“候鸟族群”涌向海南,一场酒店业的狂欢?
泡脚的益处及经期泡脚注意事项
《盗墓笔记》之前的故事:张起灵的身世来历,不一样的传奇经历
猫草对猫咪的六大好处及六种常见种类
2025年股市投资策略:分散布局把握政策红利,三大主线构建稳健组合
交通信号灯大全及图解:安全驾驶必备指南
不同年龄儿童矫正牙齿的方法有哪些?并抓住孩子矫正的较佳年龄
社交媒体在市场营销策略中的作用与挑战
紫砂壶鉴赏与选择指南:从材质到工艺的全面解析
对标国际GRI标准:中国企业ESG劳动用工合规问题探讨(下)
银的抗真菌效果,科学还是神话?
婆媳关系的智慧:她是如何化解家庭中的小矛盾的?
虚拟同步发电机(VSG)惯量阻尼自适应控制仿真模型(simulink仿真实现)
Spring Cloud Gateway限流和流量控制详解
港澳通行证与护照办理指南:轻松跨越港澳边界
情人节遇冷!当代年轻人理性选择的背后,小众活动的盛行
婴儿身高预测:我的孩子将来会长多高?
刘海广:发挥“生物多样性”法则的力量,寻找科学的新答案
【漫话中医】气温骤降,中医护航,让感冒无处遁形!
中国男篮42分复仇日本!深圳万人合唱《歌唱祖国》,郭士强变阵封死对手
南极科考40年,我国已在南极建了4座考察站,为什么还要建第5座秦岭站
与青春期孩子相处太难?不妨试试这些方法,改善亲子关系
成都合并简阳:一场战略深远的布局
孕妇可以吃猪肝吗?营养与注意事项全解析
肌肤不好当心是"肌饿型肌肤"!营养师推荐彩虹饮食法吃出好肤质
Cell | 器官移植革命性突破!基于干细胞胚胎模型和异种器官发生的全新探索
师兄啊师兄等级境界划分,师兄等级境界详解,修炼必看
支付创新:医疗保健行业中采用基于价值的新型商业模式