找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 3|回复: 0
收起左侧

一个STC单片机驱动MAX7219的音乐节拍器程序

[复制链接]
ID:39720 发表于 2026-5-3 08:10 | 显示全部楼层 |阅读模式
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);
    }
}

完整代码在压缩文件里

回复

使用道具 举报

无效楼层,该帖已经被删除
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表