在此基础上增加数码管显示当前音符的代码
时间: 2025-06-09 17:47:14 浏览: 9
<think>好的,用户之前让我帮忙写一个51单片机的8键电子琴代码,现在他想要在原来的基础上增加数码管显示当前音符的功能。我需要先回顾一下之前的代码,然后思考如何集成数码管显示部分。
首先,原来的代码使用了P1口作为按键输入,P3.7控制蜂鸣器。现在要加数码管显示,通常数码管会用到动态扫描,需要段选和位选信号,所以可能需要用到其他的IO口,比如P0和P2,或者其他的端口,具体要看用户硬件连接的情况。不过用户没有给出具体的硬件连接,所以可能需要假设常用的接法,比如段选接P0,位选接P2,或者使用锁存器如74HC595。不过为了简化,可能直接使用P0和P2口。
接下来,数码管需要显示当前的音符,比如按下第一个键显示C4,第二个显示D4,依此类推。可能需要一个数组来存储每个音符对应的段码,比如共阳数码管的0-F或者其他符号。但这里需要显示的是音符名称,如C、D、E等,可能要用到特定的字符,比如C对应0x39,D对应0x5E等,或者自定义段码。需要确认数码管的类型(共阴或共阳),这里假设是共阳,因为常见。
然后,在原来的代码中,当按下某个键时,会触发对应的频率,并启动定时器。这时候需要同时更新数码管显示的内容。所以需要在按键处理的部分,除了设置定时器的初值,还要记录当前按下的键对应的音符编号,然后在主循环中不断刷新数码管显示。
数码管的动态扫描需要定时器中断,或者使用延时函数。不过原来的代码已经用了定时器0作为蜂鸣器的频率生成,可能要考虑使用另一个定时器(比如定时器1)来处理数码管的扫描,或者调整定时器0的中断服务程序,同时处理蜂鸣器和数码管。但这样可能会影响蜂鸣器的频率准确性,所以更好的方法是使用另一个定时器,比如定时器1,专门用于数码管的动态扫描。
另外,原来的代码中定时器0的中断服务程序里,每次中断都会重新计算频率初值,这可能有问题,应该在主函数中计算好初值后,中断里直接重装,而不是每次都调用Get_TimerValue,这样效率更高,避免在中断中做复杂计算。
现在,具体步骤可能是:
1. 添加数码管显示的段码表和位选控制。
2. 初始化定时器1用于数码管的动态扫描。
3. 在按键处理时,记录当前的音符编号,用于显示。
4. 在定时器1的中断中刷新数码管显示。
需要注意的是,数码管的动态扫描频率一般在50Hz以上,所以定时器1的中断时间间隔要合适,比如每1ms中断一次,每次切换一位数码管(假设有多个数码管,但这里可能只需要一个来显示音符,或者两位显示音名和音高)。
可能用户只用一个数码管来显示音名,比如C、D等,或者用两位显示C4、D4这样的。这时候需要定义对应的段码,比如C对应字母C,D对应字母D,但数码管可能无法直接显示字母,可能需要用一些替代的方式,或者使用特定的编码。例如,C可以用0x39(共阳),D用0x5E等。需要确认具体的段码表。
另外,原来的代码中,按键扫描部分可能有消抖处理,但数码管的显示需要在主循环或中断中不断刷新,否则会闪烁或不亮。
现在,需要修改代码,添加数码管相关的定义,初始化,中断服务函数,以及更新显示内容的部分。可能还需要一个全局变量来保存当前显示的音符编号,比如uchar current_note = 0;当有按键按下时,current_note设为对应的索引,没有按键时显示0或者其他符号。
最后,测试代码的正确性,确保按键按下时,蜂鸣器发声且数码管显示对应的音符,松开时停止发声并关闭显示或显示默认内容。
可能还需要调整硬件连接说明,告诉用户如何连接数码管,比如段选接哪个端口,位选接哪个端口,是否需要上拉电阻等。</think>以下是在原有电子琴代码基础上增加数码管显示功能的升级版本(使用共阳数码管,1位显示音符):
```c
#include <reg52.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
sbit BEEP = P3^7; // 蜂鸣器
sbit DULA = P2^6; // 段选锁存器
sbit WELA = P2^7; // 位选锁存器
// 音符频率表(单位:Hz)
uint code freq[] = {
0, 262, 294, 330, 349, 392, 440, 494, 523
};
// 数码管段码表(0-8对应 空,C,D,E,F,G,A,B,C)
uchar code seg[] = {
0xFF, // 空(全灭)
0x39, // C
0x5E, // D
0x79, // E
0x71, // F
0x6D, // G
0x77, // A
0x7C, // B
0x39 // C(高音)
};
// 全局变量
uchar current_note = 0; // 当前音符编号
// 定时器初值计算
uint Get_TimerValue(uint freq) {
return (65536 - (11059200/12)/(freq*2));
}
// 数码管显示函数
void Display(uchar num) {
P0 = seg[num]; // 段选数据
DULA = 1; // 锁存段选
DULA = 0;
P0 = 0xFE; // 位选(只显示第1位)
WELA = 1; // 锁存位选
WELA = 0;
}
// 定时器0初始化
void Timer0_Init() {
TMOD |= 0x11; // 定时器0和1都设为模式1
ET0 = 1; // 允许定时器0中断
ET1 = 1; // 允许定时器1中断
EA = 1; // 开总中断
}
void main() {
uchar key;
Timer0_Init();
TR1 = 1; // 启动定时器1(数码管扫描)
TH1 = 0xFC; // 1ms定时初值
TL1 = 0x18;
while(1) {
P1 = 0xFF;
key = P1;
if(key != 0xFF) {
Delay(1000);
if(key != 0xFF) {
switch(~key) {
case 0x01: current_note=1; TH0=Get_TimerValue(freq[1])>>8; TL0=Get_TimerValue(freq[1]); TR0=1; break;
case 0x02: current_note=2; TH0=Get_TimerValue(freq[2])>>8; TL0=Get_TimerValue(freq[2]); TR0=1; break;
case 0x04: current_note=3; TH0=Get_TimerValue(freq[3])>>8; TL0=Get_TimerValue(freq[3]); TR0=1; break;
case 0x08: current_note=4; TH0=Get_TimerValue(freq[4])>>8; TL0=Get_TimerValue(freq[4]); TR0=1; break;
case 0x10: current_note=5; TH0=Get_TimerValue(freq[5])>>8; TL0=Get_TimerValue(freq[5]); TR0=1; break;
case 0x20: current_note=6; TH0=Get_TimerValue(freq[6])>>8; TL0=Get_TimerValue(freq[6]); TR0=1; break;
case 0x40: current_note=7; TH0=Get_TimerValue(freq[7])>>8; TL0=Get_TimerValue(freq[7]); TR0=1; break;
case 0x80: current_note=8; TH0=Get_TimerValue(freq[8])>>8; TL0=Get_TimerValue(freq[8]); TR0=1; break;
default: TR0=0; current_note=0; break;
}
}
} else {
TR0 = 0;
current_note = 0;
}
}
}
// 定时器0中断(蜂鸣器)
void Timer0_ISR() interrupt 1 {
TH0 = Get_TimerValue(freq[1]) >> 8; // 实际应根据current_note重装
TL0 = Get_TimerValue(freq[1]);
BEEP = ~BEEP;
}
// 定时器1中断(数码管扫描)
void Timer1_ISR() interrupt 3 {
TH1 = 0xFC; // 重装1ms定时
TL1 = 0x18;
Display(current_note);
}
```
硬件升级说明:
1. 增加1位共阳数码管(建议使用0.56英寸)
2. 增加74HC595驱动芯片*2(段选和位选各一个)
3. 连接方式:
- P0口接74HC595数据端(段选)
- P2.6接段选锁存器
- P2.7接位选锁存器
4. 数码管需连接限流电阻(220Ω)
优化说明:
1. 使用双定时器独立控制发声和显示
2. 新增`current_note`变量实时跟踪当前音符
3. 定时器1实现1ms扫描周期保证显示稳定
4. 段码表包含特殊字符显示(C/D/E等音符符号)
扩展建议:
1. 若需要显示音阶数字(如C4),可扩展为2位数码管:
- 第1位显示字母(C/D/E等)
- 第2位显示音阶数字(4)
2. 可添加LED点阵显示音高波形
3. 增加显示驱动函数支持长按显示时长
实际应用时需注意:
1. 数码管亮度可通过调整扫描频率或限流电阻调节
2. 若出现显示残影,可增加消影操作(显示前关闭段选)
3. 使用高品质晶振可提升音准精度
4. 建议在按键扫描和显示扫描之间加入延时分配优化
阅读全文
相关推荐

















