需求分析

用C51的16个矩阵按键当作两个八度的琴键

按下时发出相应音调的声音,静态数码管显示相应音符的数字。

为了解锁更多曲目,两个多的琴键设计成#4,显示时加上小数点

下图分别为1和#4的显示,其中图一上标明了各按键的对应的音符:

C51 原创电子琴 (蜂鸣器/计时器/中断/矩阵按键)-LMLPHP

C51 原创电子琴 (蜂鸣器/计时器/中断/矩阵按键)-LMLPHP

思路

每当按键按下,判断是那个按键,

在按键尚未松开时循环以下内容:启动定时器,计时的时长为“该音调频率的一半“,每过这段时间,蜂鸣器所在引脚电平取反

各音符的频率

参考:https://blog.csdn.net/weixin_34724366/article/details/80642207

C51 原创电子琴 (蜂鸣器/计时器/中断/矩阵按键)-LMLPHP

Highs 和 Lows 的计算方法:

1/(2f) s = 1000/(2f) ms = 500 / f ms

然后带入这个小程序即可

C51 原创电子琴 (蜂鸣器/计时器/中断/矩阵按键)-LMLPHP

接线图

C51 原创电子琴 (蜂鸣器/计时器/中断/矩阵按键)-LMLPHP

完整代码

#include <reg51.h>
typedef unsigned int u16;
typedef unsigned char u8; #define GPIO_KEY P1
#define GPIO_DIG P0
sbit beep = P3^0; /*******************************全局变量******* *******************/ u8 code Highs[24] = {
0xfc,0xfc,0xfd,0xfd,0xfd,0xfd,0xfe,0xfd,
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 }; //共阴的表
u8 code ledSegment [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(u16 i)
{
while(i--);
}
void keyDown()
{
//char a = 0;
GPIO_KEY = 0x0f; //高四位低电平,低四位高电平
if(GPIO_KEY!= 0x0f) //如果有按键按下
{
delay(1000); //延时防抖
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 = ~ledSegment[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;
}
}
05-17 01:44