main.c
#include <reg52.h>
#include <intrins.h>
#include "keyscan.h"
// 定时器0初始化
void Timer0_Init()
{
TMOD |= 0x01; // 定时器0,工作模式1
TH0 = 0xFC; // 1ms定时
TL0 = 0x18;
ET0 = 1; // 允许定时器0中断
TR0 = 1; // 启动定时器0
EA = 1; // 开启总中断
}
// 主函数
void main()
{
// 初始化
P0 = 0xFF;
P1 = 0xFF;
P2 = 0x00; // LED初始全灭
P3 = 0xFF;
MAX7219_Init();
Clear_Display();
Display_Speed();
Display_BeatType();
Timer0_Init();
while(1)
{
Key_Scan();
}
}
// 定时器0中断服务程序
void Timer0_ISR() interrupt 1
{
TH0 = 0xFC; // 重新装载初值
TL0 = 0x18;
timer_count++;
if(is_playing)
{
if(timer_count >= interval)
{
timer_count = 0;
// 产生节拍
Update_LEDs();
Output_Sound();
// 更新拍数
beat_count++;
if(beat_count >= beats_per_bar[beat_type])
{
beat_count = 0;
}
}
}
}
keyscan.c
#include "keyscan.h"
unsigned int interval = 0;
unsigned char is_playing = 0;
unsigned char beat_count = 0;
unsigned int timer_count = 0;
// 按键扫描
void Key_Scan()
{
static unsigned char key_up_flag = 1;
static unsigned char key_down_flag = 1;
static unsigned char key_type_flag = 1;
static unsigned char key_start_flag = 1;
// 速度+按键
if(KEY_UP == 0)
{
if(key_up_flag)
{
key_up_flag = 0;
if(beat_speed < 240)
{
beat_speed += 1;
Display_Speed();
if(is_playing)
{
// 重新计算间隔
interval = 60000 / beat_speed; // ms
}
}
}
}
else
{
key_up_flag = 1;
}
// 速度-按键
if(KEY_DOWN == 0)
{
if(key_down_flag)
{
key_down_flag = 0;
if(beat_speed > 40)
{
beat_speed -= 1;
Display_Speed();
if(is_playing)
{
interval = 60000 / beat_speed; // ms
}
}
}
}
else
{
key_down_flag = 1;
}
// 拍型选择按键
if(KEY_TYPE == 0)
{
if(key_type_flag)
{
key_type_flag = 0;
beat_type = (beat_type + 1) % 4;
beat_count = 0;
Display_BeatType();
Update_LEDs();
}
}
else
{
key_type_flag = 1;
}
// 开始/停止按键
if(KEY_START == 0)
{
if(key_start_flag)
{
key_start_flag = 0;
is_playing = !is_playing;
if(is_playing)
{
beat_count = 0;
interval = 60000 / beat_speed; // 计算每个节拍的时间间隔(ms)
timer_count = 0;
// 显示运行状态
MAX7219_WriteCmd(5, 0x80); // 在第五位显示点
}
else
{
MAX7219_WriteCmd(5, 0x00); // 显示停止状态
LED1 = LED2 = LED3 = LED4 = LED5 = LED6 = 0; // 关闭所有LED
}
}
}
else
{
key_start_flag = 1;
}
}
void delay_ms(unsigned int ms)
{
unsigned int i, j;
for(i = 0; i < ms; i++)
for(j = 0; j < 114; j++);
}
// 更新指示灯
void Update_LEDs()
{
LED1 = LED2 = LED3 = LED4 = LED5 = LED6 = 0; // 先关闭所有LED
switch(beat_type)
{
case 0: // 2/4拍
if(beat_count == 0) LED1 = 1; // 强拍
if(beat_count == 1) LED2 = 1; // 弱拍
break;
case 1: // 3/4拍
if(beat_count == 0) LED1 = 1; // 强拍
if(beat_count == 1) LED2 = 1; // 弱拍
if(beat_count == 2) LED3 = 1; // 弱拍
break;
case 2: // 4/4拍
if(beat_count == 0) LED1 = 1; // 强拍
if(beat_count == 1) LED2 = 1; // 弱拍
if(beat_count == 2) LED3 = 1; // 次强拍
if(beat_count == 3) LED4 = 1; // 弱拍
break;
case 3: // 6/8拍
if(beat_count == 0) LED1 = 1; // 强拍
if(beat_count == 1) LED2 = 1; // 弱拍
if(beat_count == 2) LED3 = 1; // 弱拍
if(beat_count == 3) LED4 = 1; // 次强拍
if(beat_count == 4) LED5 = 1; // 弱拍
if(beat_count == 5) LED6 = 1; // 弱拍
break;
}
}
// 输出音源信号
void Output_Sound()
{
// 根据拍型输出不同的音源
switch(beat_type)
{
case 0: // 2/4拍
if(beat_count == 0)
{
SOUND1 = 1; // 强拍
delay_ms(20);
SOUND1 = 0;
}
else
{
SOUND2 = 1; // 弱拍
delay_ms(10);
SOUND2 = 0;
}
break;
case 1: // 3/4拍
if(beat_count == 0)
{
SOUND1 = 1; // 强拍
delay_ms(20);
SOUND1 = 0;
}
else
{
SOUND3 = 1; // 弱拍
delay_ms(10);
SOUND3 = 0;
}
break;
case 2: // 4/4拍
if(beat_count == 0)
{
SOUND1 = 1; // 强拍
delay_ms(20);
SOUND1 = 0;
} else if(beat_count == 2)
{
SOUND2 = 1; // 次强拍
delay_ms(15);
SOUND2 = 0;
}
else
{
SOUND3 = 1; // 弱拍
delay_ms(10);
SOUND3 = 0;
}
break;
case 3: // 6/8拍
if(beat_count == 0)
{
SOUND1 = 1; // 强拍
delay_ms(20);
SOUND1 = 0;
}
else if(beat_count == 3)
{
SOUND2 = 1; // 次强拍
delay_ms(15);
SOUND2 = 0;
}
else
{
SOUND4 = 1; // 弱拍
delay_ms(10);
SOUND4 = 0;
}
break;
}
}
diplay.c
#include "display.h"
unsigned int beat_speed = 120; // 节拍速度(BPM),默认120
unsigned char beat_type = 0; // 拍型索引 0:2/4, 1:3/4, 2:4/4, 3:6/8
// 数字字体(0-9)
unsigned char code font_table[] =
{
0x7E, 0x30, 0x6D, 0x79, 0x33,
0x5B, 0x5F, 0x70, 0x7F, 0x7B
};
// 拍型对应的每小节拍数
unsigned char code beats_per_bar[] = {2, 3, 4, 6};
// MAX7219初始化命令
unsigned char code init_cmds[][2] =
{
{0x0C, 0x01}, // 关闭关机模式
{0x0B, 0x07}, // 扫描所有8位
{0x0A, 0x08}, // 亮度
{0x09, 0x00}, // 解码模式:无
{0x0F, 0x00} // 显示测试:关闭
};
// MAX7219写一个字节
void MAX7219_WriteByte(unsigned char data_byte)
{
unsigned char i;
for(i = 0; i < 8; i++)
{
MAX7219_CLK = 0;
MAX7219_DIN = (data_byte & 0x80) ? 1 : 0;
data_byte <<= 1;
MAX7219_CLK = 1;
}
}
// MAX7219写命令
void MAX7219_WriteCmd(unsigned char address, unsigned char data_byte)
{
MAX7219_CS = 0;
MAX7219_WriteByte(address);
MAX7219_WriteByte(data_byte);
MAX7219_CS = 1;
}
// MAX7219初始化
void MAX7219_Init()
{
unsigned char i;
for(i = 0; i < 5; i++)
{
MAX7219_WriteCmd(init_cmds[i][0], init_cmds[i][1]);
}
}
// 在数码管上显示数字
void Display_Number(unsigned char digit, unsigned char number)
{
if(digit >= 1 && digit <= 8)
{
MAX7219_WriteCmd(digit, number);
}
}
// 显示当前速度
void Display_Speed()
{
unsigned char bai, shi, ge;
bai = beat_speed / 100;
shi = (beat_speed % 100) / 10;
ge = beat_speed % 10;
// 显示在4-6位数码管上
Display_Number(3, font_table[bai]);
Display_Number(2, font_table[shi]);
Display_Number(1, font_table[ge]);
}
// 显示拍型
void Display_BeatType()
{
switch(beat_type)
{
case 0: Display_Number(8, 0x6D); //2
Display_Number(7, 0x01);
Display_Number(6, 0x33); break; // 4
case 1: Display_Number(8, 0x79); // 3
Display_Number(7, 0x01);
Display_Number(6, 0x33); break; // 4
case 2: Display_Number(8, 0x33); // 4
Display_Number(7, 0x01);
Display_Number(6, 0x33); break; // 4
case 3: Display_Number(8, 0x5F); // 6
Display_Number(7, 0x01);
Display_Number(6, 0x7F); break; // 8
}
}
// 清除所有显示
void Clear_Display()
{
unsigned char i;
for(i = 1; i <= 8; i++)
{
MAX7219_WriteCmd(i, 0x00);
}
}
完整代码在压缩文件里
|