问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

基于51单片机的电子琴设计项目详解

创作时间:
作者:
@小白创作中心

基于51单片机的电子琴设计项目详解

引用
CSDN
1.
https://blog.csdn.net/2301_80765253/article/details/139389015

本文将介绍一个基于51单片机的电子琴设计项目。通过定时器产生不同的频率来控制蜂鸣器发出不同的音高,并使用矩阵键盘来实现音符的输入。文章详细介绍了硬件准备、硬件连接、代码编写以及成果展示等内容,适合对单片机编程感兴趣的读者学习参考。

前言

51单片机开发板自带了蜂鸣器,但是无源的,本次设计是通过定时器产生不同的频率来使蜂鸣器产生不同的音高,并让矩阵键盘来控制音高的发生。

一、硬件准备

选择一个51单片机,如AT89C51。
准备一些按键,如4x4矩阵键盘,用于输入音符。
准备一个音频输出设备,如蜂鸣器或小型扬声器,连接到单片机的某个GPIO引脚(这里单片机自带)。
如有需要,还可以添加其他硬件,如LED灯、LCD显示屏等。

二、硬件连接

定义数码管、矩阵键盘、蜂鸣器引脚。

代码如下(示例):

#define GPIO_KEY P1 //定义矩阵键盘I/O口
#define GPIO_DIG P0 //定义数码管I/O口     
sbit beep = P2^5;   //定义蜂鸣器银胶

三、代码编写

1、初始化GPIO引脚和定时器。
2、编写按键扫描函数,用于检测哪个按键被按下。
3、编写音频输出函数,根据按键值计算音符频率,并使用定时器或PWM产生音频信号。
4、在主循环中调用按键扫描函数和音频输出函数。

(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程序的逻辑。

总结

本次设计只是简单的利用定时器与键盘控制蜂鸣器发出声音,代码可能也有些许问题,后期水平提升了再修改一下吧;本期主要是学习定时器工作原理及使用、数码管显示、矩阵键盘扫描、频率计算等。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号