×
单片机 > 单片机程序设计 > 详情

【C51】源码 4 -- 喇叭播放【寒蝉鸣泣之时】插曲 You

发布时间:2020-06-09 发布时间:
|
学单片机到喇叭部分,觉得挺好玩,编写了喇叭播放音乐的程序……

BGM 取自【寒蝉鸣泣之时】插曲 You,简谱由网络提供,感谢作者……先把简谱附上:

还有一张自制的“钢琴按键频率分布”,没写那么全,只对应本程序,图如下:

附上源码:(初出茅庐,难免有写的不好的地方,仅作备份之用,欢迎指点,喷子退散……)

/*******************************************************************
                               喇叭播放音乐

BGM 取自【寒蝉鸣泣之时】的插曲 You

缺点:

1、没有添加调节音量的功能
2、音高、节拍 都要自己提前写好,工作量大
      可以改进成:通过解析 midi 文件进行音乐播放
     (midi 里边就是音高、节拍信息)
********************************************************************/

#include 

sbit SPK = P1^0;    // 喇叭输入

/************************************************************************************************************************************
PIANO_F[ ]:是钢琴按键的频率,数组下标对应钢琴按键,如上图所示
               例:图上可以看出:8 号键对应钢琴的中央 C,正好是 PIANO_F[8],对应频率为 261.6 Hz
               注:PIANO_F[0] 不代表任何按键,为了方便计数,数组下标从 1 开始有意义
*************************************************************************************************************************************/

float code PIANO_F[ ] = {7.63, 
                                          130.8,  146.8,  164.8,  174.5,  196.0,  220.0,  247.1, 
                                          261.6,  293.6,  329.6,  349.0,  392.0,  440.0,  494.0, 
                                          523.2,  587.2,  659.2,  698.3,  784.0,  880.0,  988.0, 
                                          1046.4, 1174.4, 1318.4, 1396.8, 1567.7, 1760.0, 1975.3};

/******************************************************************************************************
PITCH[ ]:指的是 音高,对应上图中的按键号
          例:中音 do:如图对应 8 号键
                  高音 do:如图对应 15 号键
      好处:假如歌曲播放到了第 i 个音,那么 PITCH[i] 就是对应钢琴上的按键
                  那么 PIANO_F[ PITCH[i] ] 就是对应的琴键的频率,方便编程使用
          注:255 代表 音乐结束
                  0 代表 八分休止符
******************************************************************************************************/

unsigned char code PITCH[ ] = {16, 17, 16, 15, 16, 12, 17, 16, 15, 
                                                       16, 17, 16, 15, 15, 19, 16, 
                                                       16, 17, 16, 15, 16, 12, 17, 16, 15, 
                                                       16, 17, 16, 15, 15, 12, 
                                                       12, 10, 12, 15, 15, 
                                                       16, 16, 16, 17, 17, 12, 
                                                       12, 10, 12, 0,  15, 15, 16, 
                                                       16, 16, 16, 19, 17, 12, 
                                                       12, 10, 0,  12, 0,  15, 15, 15, 
                                                       16, 16, 16, 17, 17, 12, 
                                                       12, 10, 0,  12, 0,  15, 15, 16, 
                                                       16, 16, 16, 19, 17, 26, 
                                                       26, 24, 0,  26, 26, 22, 0,  23, 
                                                       23, 22, 23, 0,  24, 23, 24, 26, 
                                                       26, 24, 0,  26, 26, 22, 0,  23, 
                                                       23, 22, 0,  23, 22, 
                                                       255};

/***************************************************************************************************************
BEAT[ ]:指的是 节拍,数值代表基本节拍的倍数,这里基本节拍为 十六分音符 长
         例:8,代表 8 倍 十六分音符 长,即:二分音符
                4,代表 4 倍 十六分音符 长,即:四分音符
                2,代表 2 倍 十六分音符 长,即:八分音符
                1,代表 1 倍 十六分音符 长,即:十六分音符
                6,代表 6 倍 十六分音符 长,即:符点四分音符
***************************************************************************************************************/

unsigned char code BEAT[ ] = {4, 1, 1, 2, 2, 2, 1, 1, 2,    4, 1, 1, 2, 2, 2, 4, 
                                                      4, 1, 1, 2, 2, 2, 1, 1, 2,    4, 1, 1, 2, 6, 2, 
                                                      2, 4, 2, 6, 2,                    2, 2, 2, 2, 6, 2, 
                                                      2, 2, 4, 2, 2, 2, 2,            2, 2, 2, 2, 6, 2, 
                                                      2, 2, 2, 2, 2, 2, 2, 2,        2, 2, 2, 2, 6, 2, 
                                                      2, 2, 2, 2, 2, 2, 2, 2,        2, 2, 2, 2, 6, 2, 
                                                      2, 2, 2, 2, 2, 2, 2, 2,        2, 2, 2, 2, 2, 2, 2, 2, 
                                                      2, 2, 2, 2, 2, 2, 2, 2,        2, 2, 2, 2, 8, 
                                                      255};

unsigned char time_h = 0;      // 用于向定时器中断程序传递下一步定时信息
unsigned char time_l = 0;

unsigned int time = 0;             // 根据按键频率,换算出应该定时多长时间
unsigned char i;

void Init_Timer0(void);                   // 初始化 定时器 0
void Delay(unsigned int t);            // 普通延时
void Delay_ms(unsigned int t);     // 相当近似的延迟 t ms

void main (void)
{
    Init_Timer0();       

    for (i = 0; PITCH[i] != 255; i++) {         // 顺序播放,255 为音乐结束

        TR0 = 0;          // 关 定时器0

        Delay_ms(20);          // 延时一段时间,保证按键与按键之间能分得清楚

       /*********************************************************************************************************************
        定时时间 time 的计算:

        以 中央 C 为例:频率为 261.6 Hz,就是每秒 261.6 下,倒数就是 每下 1/261.6 s,
                                      换成 us 就是 1,000,000/261.6,每响一次是一个上升沿加一个下降沿,
                                      因此,变换的次数应该是 2 倍,每次变换所花时间是一半,
                                      即:1,000,000/(2*261.6)
        **********************************************************************************************************************/

        time = (unsigned int)(1000000 / (2 * PIANO_F[PITCH[i]]));

        time_h = (65536 - time) / 256;
        time_l = (65536 - time) % 256;

        TH0 = time_h;         // 定时器赋值 
        TL0 = time_l;

        TR0 = 1;                   // 开 定时器0

       /****************************************************************************************************************
        如果音高是 0,说明是 八分休止符,关定时器,定时器不驱动喇叭,也就没有声音了
        ****************************************************************************************************************/

        if (PITCH[i] == 0) TR0 = 0;

       /********************************************************************************************************
        相当重要的延时!节拍全部由它控制,不是很精确,其实用 定时器 1 可能更好
        ********************************************************************************************************/

        Delay_ms(BEAT[i] * 30);
    }

    TR0 = 0;        // 音乐播放完后,关定时器,收尾

    /*****************************************************************************************************************
    相当重要!没有此语句将导致循环执行 main 函数!

    详细讨论参见:http://gaebolg.blog.163.com/blog/static/19826906820122254823928/
    *****************************************************************************************************************/

    while (1);
}

void Init_Timer0(void)
{
    TMOD = 0x01;       // 定时器 0,模式1
    EA = 1;                   // 开 总中断
    ET0 = 1;                 // 开 定时器 0 中断 
}

void Timer0_ISR(void) interrupt 1
{
    TH0 = time_h;      // 重装初值
    TL0 = time_l;

    SPK = !SPK;         // 产生方波,驱动喇叭发声
}

void Delay(unsigned int t)
{
    while (t--);
}

void Delay_ms(unsigned int t)       // 根据测试,可以相当近似的表示 t ms
{
    while (t--) {
        Delay(245);
        Delay(245);
    }
}

关键字:C51  喇叭播放

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
STM32中断向量表的位置.重定向